我是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;
}
}
答案 0 :(得分:2)
在Windows窗体中,您可以使用Control.Invoke Method来影响后台线程的UI。如果没有单独的方法,你可以使用lambda表达式:
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
方法的委托。您有机会创建所需的任何方法签名。
另请查看Thread Synchronization (C# Programming Guide)。一旦代码尝试在线程之间共享一些变量,您就需要这个。
答案 1 :(得分: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 /方法来实现。
答案 2 :(得分:1)
两个步骤可以让你更接近:
1)将需要更新UI的任何代码分离为单独的方法
2)在您的DoWork方法中,当您想要调用方法来更新UI时,使用Dispatcher.Invoke
来调用将更新UI的方法。这可以确保该方法在UI的线程中执行。