Introduction
I don't intend for this article to teach multi-threaded programming in .NET,
I do want to point out some important things when dealing with multi-threaded
programs in a Windows Forms environment. This article will be code light,
however I have provided a full sample illustrating how to employ the practices
required of a Multi-Threaded Windows Forms application.
What not to do
First and foremost (and the reason I'm writing this article) is that you
absolutely, positively, cannot interact with UI elements from a thread
that did not create them. You may get away with it in some cases, but it
will fail at some point and then heads will roll because you will have a lot of
code to change. Now you may ask how should you interact with the UI from
another thread.
What you should do
Luckily the designers for Windows Forms realized the plight that may be
caused without being able to interact with the UI from another thread. So
they add the Invoke
method to the Control class. Invoke
runs the specified
delegate on the thread that create the control's window (or the parents window if
it doesn't have one).
public void Invoke(System.Delegate delegate);
public void Invoke(System.Delegate delegate, object [] args);
The second form of Invoke
takes an object array containing any parameters you
wish to be passed into the delegate. For those still not used to
delegates, think of them as the .NET way to use function pointers.
A brief example
A simple example of using this is to create a new form, add a textbox and a
button to it. Call the textbox myTextbox
.
At the top of the code file add another using statement for the threading
library.
using System.Threading;
In the button's Click event place the following code.
Thread t = new Thread(new ThreadStart(SayHiThread));
t.IsBackground = true;
t.Start();
Now add this code to the form
private void SayHiThread()
{
Invoke(new SayHiDelegate(SayHi), new object [] { "Hello from the thread" });
}
private void SayHi(string msg)
{
myTextbox.Text = msg;
}
Outside of the form's class add this declaration
public delegate void SayHiDelegate(string msg);
Run the application and click the button, the textbox should now say "Hello
from the thread".
What it did
Well that probably confused you more than it helped so I'll try to explain
piece by piece what it does.
The Invoke
method requires an instance of a delegate. A delegate is
nothing more than a type-safe function pointer. You declare a delegate by
creating the function signature you wish to have, then preceding the signature
with the delegate
keyword. In the example above the function will take a
string as its only parameter and return nothing. You create a new delegate
instance as if you were creating an instance of a class, except you pass in the
name of the method you want the delegate to call.
The code inside of the button's click event creates a new thread, sets it as
a background thread, then runs it. All the thread does is run the function
SayHiThread
. The thread only does one thing, it calls Invoke
on the form
(since SayHiThread
is a member of the form it calls the form's Invoke
method).
The Invoke
method takes an instance of a delegate, and an object array
containing the parameters to give to the delegate. In this case there is
only one parameter; a string.
To sum it up, Invoke
calls the function which the delegate points to, passing
in the parameters in the object array.
Summary
When you get down to it the hardest part of writing the code to interact with
the UI from another thread is getting to understand delegates and how to use
them. As always, if there are any questions e-mail me or post them below.
The source/demo show a example that's a bit more advanced, but still nothing too advanced.