5

私が開発しようとしているフォームには、6 つの画像ボックスの配列と 6 つのダイ画像の配列があります。クリックすると、サイコロを「転がす」6つのスレッドを作成し、各画像を一瞬表示する必要があるボタンがあります。私が抱えている問題は、サイコロが振られた後、ボタン クリック内でメソッドを呼び出す必要があることです。サイコロを振ることはできますが、すぐにメッセージ ボックスが表示されます。私はいくつかの異なる方法を試しましたが、さまざまなエラーが発生しました。以下の動作しないバージョンでは、プログラムがフリーズします。私はたくさんのリソースをチェックアウトしましたが、デリゲートやインボークなどのいくつかの概念を十分に把握していません。どんな助けでも素晴らしいでしょう!これが私のプログラムです

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
4

3 に答える 3

2

画像を設定する呼び出しと画像ボックスの更新の間にデッドロックが発生しているようです。

プログラムを少し考え直すことをお勧めします。あなたのプログラムは、個々のスレッドで個々のダイをモデル化しているという概念に基づいて構築されているようです。糸の状態からダイスの状態を分解します。たとえば、IsRolling や CurrentValue などの特定の状態を持つ Die クラスを作成したい場合があります。ワーカー スレッドのループ内で、そのクラス (およびそのクラスのみ) のオブジェクトを使用および変更します。そうすれば、UI スレッドを呼び出して更新する必要がなくなります。依存関係はそのようにずっときれいです。タイマーを作成したい場合があります定期的に (1 秒間に 10 ~ 30 回) 起動する UI スレッドで、各サイコロの状態を読み取り、そのように画像を更新します。循環的な依存関係がないため、デッドロックに関してははるかに安全です。また、ダイの画像がよりスムーズで予測可能な方法で更新されるため、より魅力的なインターフェイスが生成される可能性があります。

別の経験則... Thread.Abort() を呼び出さないでください(参考文献を参照)。通常、Die オブジェクトのプロパティを使用し、それを読み取って UI を更新する方がはるかに安全です。

于 2012-12-06T01:26:52.210 に答える
0

MessageBox.Show("Each die has its own thread");から削除する必要がありますbutton1_Click

返されたスレッドの数を追跡するプロパティを作成します。それが6のinvokeに達したときMessageBox.Show("Each die has its own thread");(おそらく、この呼び出しを独自のメソッドに入れて、そのメソッドを呼び出すことをお勧めします)。

問題は、スレッドを開始し、実行中にスレッドが戻るのを待つのではなく、メッセージボックスを表示することです。

于 2012-12-06T01:27:14.770 に答える
-1

最新バージョンの .Net Framework を操作できる場合は、System.Threading.Tasks名前空間を使用することをお勧めします。良い点は、マルチスレッドの詳細の多くをカプセル化し、物事をよりクリーンにすることです。簡単な例を次に示します。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TasksExample
{
    class Program
    {
        static void Main(string[] args)
        {
            // holds all the tasks you're trying to run
            List<Task> waitingTasks = new List<Task>();

            // a simple object to lock on
            object padlock = new object();

            // simple shared value that each task can access
            int sharedValue = 1;

            // add each new task to the list above.  The best way to create a task is to use the Task.Factory.StartNew() method.
            // you can also use Task.Factory<RETURNVALUE>.StartNew() method to return a value from the task
            waitingTasks.Add(Task.Factory.StartNew(() => 
            {
                // this makes sure that we don't enter a race condition when trying to access the
                // shared value
                lock (padlock)
                {
                    // note how we don't need to explicitly pass the sharedValue to the task, it's automatically available
                    Console.WriteLine("I am thread 1 and the shared value is {0}.", sharedValue++);
                }
            }));

            waitingTasks.Add(Task.Factory.StartNew(() => 
            {
                lock (padlock)
                {
                    Console.WriteLine("I am thread 2 and the shared value is {0}.", sharedValue++);
                }
            }));

            waitingTasks.Add(Task.Factory.StartNew(() => 
            {
                lock (padlock)
                {
                    Console.WriteLine("I am thread 3 and the shared value is {0}.", sharedValue++);
                }
            }));

            waitingTasks.Add(Task.Factory.StartNew(() => 
            {
                lock (padlock)
                {
                    Console.WriteLine("I am thread 4 and the shared value is {0}.", sharedValue++);
                }
            }));

            waitingTasks.Add(Task.Factory.StartNew(() => 
            {
                lock (padlock)
                {
                    Console.WriteLine("I am thread 5 and the shared value is {0}.", sharedValue++);
                }
            }));

            waitingTasks.Add(Task.Factory.StartNew(() => 
            {
                lock (padlock)
                {
                    Console.WriteLine("I am thread 6 and the shared value is {0}.", sharedValue++);
                }
            }));


            // once you've spun up all the tasks, pass an array of the tasks to Task.WaitAll, and it will
            // block until all tasks are complete
            Task.WaitAll(waitingTasks.ToArray());

            Console.WriteLine("Hit any key to continue...");
            Console.ReadKey(true);
        }
    }
}

これがお役に立てば幸いです。さらにサポートが必要な場合はお知らせください。

于 2012-12-06T01:31:28.030 に答える