Invoking from a thread works but can't be done simpler?
I managed to make a simple program that updates a TextBox from a different thread and class. The most simple way to learn threading in a visual way. ;)
The code is made with the documentation from msdn. It runs and I see no memory leak. But I have a feeling this can be done simpler.
There are two main classes; the Form and a BackGround class. In the BackGround class is a function called generateStream that updates a TextBox in the Form class via a delegate to a function called updateTextBox.
When buttonStart is pressed there are two actions performed. First generateStream is called directly. When generateStream is ready a thread is made that runs generateStream again (via TheFunction). When using a different thread, TextBox requires a invoke solution.
All this is implemented and works. I think it's quite a lot of code and can perhaps be done more clean.
The function in the mainclass that updates the form and is called back.
public void updateTextBox(string strtext)
{
if (this.InvokeRequired) //When calling from another thread; invoke is required.
{
SetTextCallBack cb = new SetTextCallBack(updateTextBox);
this.Invoke(cb, new object[] { strtext });
}
else //Call can be performed safely.
{
textBoxStatus.SelectionStart = 开发者_JS百科textBoxStatus.Text.Length;
textBoxStatus.AppendText(strtext);
textBoxStatus.Update();
}
}
The actions done by the click of the button.
private void buttonStart_Click(object sender, EventArgs e)
{
buttonStart.Enabled = false;
/*
* Step 1: Let's call the function blocking.
* The GUI will not respond to mouse geastures. Hoover mouse over inputbox to see no change in pointer.
*/
bg.generateStream(numberOfCalls, "GUI Thread: ");
/*
* Step 2: Let's call the function in a seperate thread.
* The GUI will now respond to the mouse. Hoover mouse over inputbox to see pointer change.
*/
Thread sepThread = new Thread(new ThreadStart(this.TheFunction));
sepThread.Start();
buttonStart.Enabled = true;
}
The generateStream function in the BackGround class.
public void generateStream(int amountOfStreams, string inpString)
{
for (int i = 0; i < amountOfStreams; i++)
{
Thread.Sleep(1000); //Easy does it.
myCallBack(inpString + i.ToString() + System.Environment.NewLine); //This is the text to the Main Form.
}
}
When using a thread I use now two callbacks to update the TextBox. One from the BackGround class to the Form, and one to implement Invoke. Is there for instance no semaphore-solution possible that my updateTextBox function can have only one thread access?
There is no harm in calling Invoke when InvokeRequired is true. I also like to use anonymous methods in C# 2.0. You can simplify your function as follows:
private delegate void SimpleProc();
private void updateTextBox(string strtext) {
this.Invoke(new SimpleProc(delegate() {
textBox1.SelectionStart = textBox1.Text.Length;
textBox1.AppendText(strtext);
textBox1.Update(); // only needed if updateTextBox is called from UI thread
}), null);
}
Replacing Invoke with BeginInvoke is useful for a fire-and-forget scenario, where you don't want the background thread to wait for the UI thread to complete the update, but you need to be sure if this is OK for your application.
The updated function can be directly called from either the UI thread or your background thread. No callbacks are needed; the only callback used is the anonymous function inside of updateTextBox. It really doesn't get any simpler than this. In fact, it's simple enough that the need for creating a separate function for invoking the UI thread is not necessary in many cases.
精彩评论