3

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; } } 
2
  • Is this WPF or Windows Forms? Commented Nov 30, 2012 at 3:49
  • Thanks for this, though i've still not been able to put it into practice it looks like a good starting place. Commented Dec 4, 2012 at 23:54

3 Answers 3

2

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.

Sign up to request clarification or add additional context in comments.

4 Comments

FYI, don't pass ex.Message. Pass the entire exception. There's usually a lot more information in there. If you need to log a string, just log ex.ToString().
Duly noted and edited accordingly. Good call, John! Arguably, the 'this' qualifiers can be nixed too.
I have upvoted, but you'd make me happier if you added a "throw;" after calling HandleError. Right now, the code will continue even if the exception is a really bad one.
I would agree, but that's more of a question of implementation. In any case - for all who may read, look here for some gotchas about handling exceptions in multithreaded applications.
2

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.

Comments

1

Two steps that should get you a lot closer:
1) Separate any code that needs to update UI into individual methods.
2) From your DoWork method, when you want to call a method to update UI, use Dispatcher.Invoke to call the method that is going to update the UI. This ensures that that method executes in the UI's thread.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.