The form I'm trying to develop has an array of 6 picture boxes and an array of 6 die images. I have a button that when clicked needs to create 6 threads that "roll" the dice, showing each image for a moment. The problem I'm having is that I need to call a method within button click after the dice have been rolled. I can get the dice to roll but the message box is displayed immediately. I've tried a few different ways and get various errors. In the non working version below, the program freezes. I've checked out a ton of resources but I'm just not grasping some concepts like Delegates and Invoke all that well. Any help would be great! Here's my program
namespace testDice
{
public partial class Form1 : Form
{
private Image[] imgAr;
private PictureBox[] picBoxAr;
private Random r;
private Thread[] tArray;
private ThreadStart tStart;
private delegate void setTheImages();
public Form1()
{
InitializeComponent();
setImageArray();
setPicBoxAr();
}
private void setImageArray()
{
imgAr = new Image[6];
imgAr[0] = testDice.Properties.Resources.die6;
imgAr[1] = testDice.Properties.Resources.die1;
imgAr[2] = testDice.Properties.Resources.die2;
imgAr[3] = testDice.Properties.Resources.die3;
imgAr[4] = testDice.Properties.Resources.die4;
imgAr[5] = testDice.Properties.Resources.die5;
}
private void setPicBoxAr()
{
picBoxAr = new PictureBox[6];
picBoxAr[0] = pictureBox1;
picBoxAr[1] = pictureBox2;
picBoxAr[2] = pictureBox3;
picBoxAr[3] = pictureBox4;
picBoxAr[4] = pictureBox5;
picBoxAr[5] = pictureBox6;
}
private void button1_Click(object sender, EventArgs e)
{
roll();
//wait for threads to finish and update images--doesn't work
for (int n = 0; n < 6; n++)
{
while (tArray[n].IsAlive)
{
for (int i = 0; i < 6; i++)
{
this.picBoxAr[i].Update();
}
}
}
MessageBox.Show("Each die has its own thread");
}
private void roll()
{
this.tStart = new ThreadStart(RunAllDiceThreads);
this.tArray = new Thread[6];
for (int i = 0; i < 6; i++)
{
this.tArray[i] = new Thread(tStart);
this.tArray[i].Start();
}
}
private void RunAllDiceThreads()
{
int n = 0;
while (n < 50)
{
setImg();
Thread.Sleep(50);
n++;
}
for (int i = 0; i < 6; i++)
{
if (tArray[i] != null)
{
tArray[i].Abort();
tArray[i] = null;
}
}
}// end RunAllDiceThreads
private void setImg()
{
r = new Random();
for (int i = 0; i < 6; i++)
{
if (this.picBoxAr[i].InvokeRequired)
{
setTheImages s = new setTheImages(setImg);
// parameter mismatch error here
//this.Invoke(s, new object[] { imgAr[r.Next(6)] });
//Freezes here!!
this.Invoke(s);
}
else
{
this.picBoxAr[i].Image = imgAr[r.Next(6)];
}
}
}//end setImg
}// end class Form1
}//end namespace testDice
Sounds like you're getting a deadlock between your invocation of setting the images and your update of the picture boxes.
I'd recommend rethinking your program a bit. Your program almost seems to be built on the concept that you're modeling an individual die with an individual thread. Break up the state of the die from the state of the thread. For example, you might want to create a Die class which has a certain state to it, such as IsRolling, or CurrentValue. Use and modify objects of that class (and that class only) inside your loops in your worker threads. That way, you won't have to invoke back to your UI thread to update. The dependencies are a lot cleaner that way. You might want to create a Timer in your UI thread which periodically fires (say 10-30 times a second), reads the state of each of the dice, and updates the images that way. That's a lot safer in terms of deadlocks because you don't have any cyclic dependencies. It'll also likely produce a more attractive interface because your die images will update in a smoother, more predictable fashion.
Another rule of thumb... Don't call Thread.Abort() (see references). It's generally a lot safer to use a property of a Die object and simply read from that to update your UI.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With