3

私はC#とスレッド化に不慣れで、マルチスレッド化する必要があるReversi(othello)のゲームを持っているプロジェクトがあります。何日も実験した後、私はそれ以上前進しません。UIをコードから分離する必要があります。基本的に、ロードに時間がかかるプロセスがプログラムを「フリーズ」せず、ユーザーがボタンとユーザーインターフェイスビットにアクセスできるようにします。

バックグラウンドワーカー、タスク、一般的なスレッドを調べました。

コードのいくつかの重要なセクションを次に示します。移動ボタンをクリックすると、doworkメソッドを実行して、UIをインタラクティブなままにします。

    private void playerMoveButton_Click(object sender, EventArgs e)
    {
        _bw = new BackgroundWorker
        {
            WorkerReportsProgress = true,
            WorkerSupportsCancellation = true
        };
        _bw.DoWork += bw_DoWork;
        if (_bw.IsBusy) _bw.CancelAsync();
    }

  public void bw_DoWork(object sender, DoWorkEventArgs e)
    {

        if (gameOver)
        {
            moveLabel.Text = "Game Over";
            moveTypeLabel.Text = "";
            return;
        }

        // Now the computer plays
        moveLabel.Text = "My Move";

        // Does it have any legal moves?
        int numComputerMoves = theGame.CountLegalMovesMoves();
        if (numComputerMoves != 0)
        {
            if (computerPlayStyle == PlayStyle.RANDOM)
            {
                moveTypeLabel.Text = "Guessing...";

                moveTypeLabel.Visible = true;
                this.Refresh();
                // Sleep for a little to give player time to see what's
                // going on
                Thread.Sleep(1000);
                // get a move at random
                int[] movePos = theGame.FindRandomMove();
                // make move
                theGame.MakeMove(movePos[0], movePos[1]);
                boardLayoutPanel.Refresh();
            }
            else
            {
                moveTypeLabel.Text = "Thinking...";

                moveTypeLabel.Visible = true;
                this.Refresh();

                // Get best move
                int[] movePos = theGame.FindGoodMove(minimaxDepth);
                // make move
                theGame.MakeMove(movePos[0], movePos[1]);
                boardLayoutPanel.Refresh();
            }
        }
        else
        {
            moveTypeLabel.Text = "I've no legal moves.";

            moveTypeLabel.Visible = true;
            this.Refresh();
            // Sleep for a little to give player time to see what's
            // going on
            Thread.Sleep(1000);
            // Change current player
            theGame.SwapCurrentPlayer();
        }

        //Reset for the player move
        moveLabel.Text = "Your Move";

        int blackScore = theGame.BlackCellCount();
        int whiteScore = theGame.WhiteCellCount();

        string bscoreMsg = "Black: " + blackScore;
        string wscoreMsg = "White: " + whiteScore;

        blackScoreLabel.Text = bscoreMsg;
        whiteScoreLabel.Text = wscoreMsg;

        // Does player have any legal moves

        int numPlayerMoves = theGame.CountLegalMovesMoves();

        if (numPlayerMoves == 0)
        {
            moveTypeLabel.Text = "You have no legal moves.";
            playerMoveOKButton.Visible = true;
            // If computer player has no legal moves game over!
            if (numComputerMoves == 0)
            {
                gameOver = true;
            }
        }
        else
        {
            moveTypeLabel.Text = "Select cell or choose";
            randomMoveButton.Visible = true;
            playerMoving = true;
        }
    }
4

3 に答える 3

2

Windowsフォームでは、Control.Invokeメソッドを使用して、バックグラウンドスレッドからUIに影響を与えることができます。個別のメソッドを作成する意味がない場合は、ラムダ式を使用できます。

public void bw_DoWork(object sender, DoWorkEventArgs e)
{
    string str;
    //count str value...
    textBox1.Invoke(new Action<string>((s) =>
                    {
                         textBox1.Text = s;
                    }),
                    str);
}

または、別の方法を作成することもできます。

private void SetText(string s)
{
    textBox1.Text = s;
}

次のように使用します。

textBox1.Invoke(new Action<string>(SetText), str);

Action Delegateを使用して、Invokeメソッドへのデリゲートを指定しました。必要なメソッドシグネチャを作成する機会があります。


スレッド同期(C#プログラミングガイド)もご覧ください。コードがスレッド間でいくつかの変数を共有しようとするとすぐに、これが必要になります。

于 2012-11-30T03:57:17.517 に答える
2

代理人が必要です。

デリゲートを使用してこれを行う方法の非常に簡単な例を次に示します。

    public partial class Form1 : Form
    {
        private Thread _worker;
        private delegate void SendErrorDelegate(Exception exception);
        private delegate void SetCurrentStatus(string status);
        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                 this._worker = new Thread(new ThreadStart(MyMethod));
                 this._worker.Start();
            }
            catch (Exception ex)
            {
                HandleError(ex);
            }
        }
        private void MyMethod()
        {
            //you can do work like crazy here, but any time you want to update UI from this method,
            //it should be done with a delegate method like:
            SetStatusLabel("this happened");
            //and
            SetStatusLabel("I just did something else");
        }
        private void SetStatusLabel(string status)
        {
           if (this.InvokeRequired)
           {
                this.Invoke(new SetCurrentStatus(SetStatusLabel), status);
                return;
           }
           this.lblStatusLable.Visible = true;
           this.lblStatusLabel.Text = status;
        }
        private void HandleError(Exception ex)
        {
             if (this.InvokeRequired)
             {
                this.Invoke(new SendErrorDelegate(HandleError), ex);
                return;
             }
             //update some UI element from delegate now. eg-
             this.txtExceptionBox.Text = ex.Message; //or ex.ToString() etc 
             //LogException(exception);               
         }
    }      

つまり、基本的に、別のスレッドでスピンアウトされたメソッドのコンテキスト内からUIを更新する場合は、必要に応じてデリゲートvoid/methodを呼び出すことで更新するようにしてください。

于 2012-11-30T04:05:36.880 に答える
1

非常に近づくための2つのステップ:
1)UIを更新する必要のあるコードを個々のメソッドに分離します。
2)DoWorkメソッドから、UIを更新するメソッドを呼び出す場合は、UIを更新するDispatcher.Invokeメソッドを呼び出すためにを使用します。これにより、そのメソッドがUIのスレッドで確実に実行されます。

于 2012-11-30T03:28:12.037 に答える