I'm new to C# and threading and i have project where i have a game of Reversi(othello) which i have to make multi-threaded, after experimenting for days I'm no further forward. I have to separate the UI from the code, basically so the processes which take a while to load do not 'freeze' the program, allowing the user to access the buttons and user interface bits.
I've looked into background worker, tasks, general threads.
Here's a couple of key sections of code, when i click the move button i want the dowork method to run, leaving the UI interactive.
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;
}
}
You want delegates.
Here's a fairly simple example of how you can go about doing this with delegates:
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);
}
}
So basically - any time you want to update the ui from within the context of the method spun out on another thread, you simply make sure to do so by invoking the delegate void/method as needed.
In Windows Forms you can use Control.Invoke Method to influence UI from background threads. You can either use lambda expressions, if there is no sence in making separate methods:
public void bw_DoWork(object sender, DoWorkEventArgs e)
{
string str;
//count str value...
textBox1.Invoke(new Action<string>((s) =>
{
textBox1.Text = s;
}),
str);
}
Or you can make a separate method:
private void SetText(string s)
{
textBox1.Text = s;
}
and use it like:
textBox1.Invoke(new Action<string>(SetText), str);
I used Action Delegate to specify a delegate to the Invoke method. It fives you an opportunity to create any method signature you need.
Also have a look at Thread Synchronization (C# Programming Guide). You'll need this, as soon as your code tries to share some variables among threads.
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