1. Creating Threads
To spawn a new thread, you need to create a new Thread object and associate it with a method that is referred to as the thread method. The new Thread
object executes the method on a separate thread. The thread terminates
once the thread method returns. The thread method can be a static or an
instance method, can be public or private, and can be called on your
object or on another. The only requirement is that the thread method
should have either one of these signatures:
void <MethodName>( );
void <MethodName>(object argument);
depending on whether or not you want to pass in an argument.
If you want to use an argument-less thread method, you associate a Thread object with the thread method by using a dedicated delegate called ThreadStart, defined as:
public delegate void ThreadStart( );
One of the Thread class constructors accepts as a single construction parameter an instance of the ThreadStart delegate, which targets the thread method:
public sealed class Thread
{
public Thread(ThreadStart start);
//Other methods and properties
}
Once you've created a new Thread object, you must explicitly call its Start( ) method to have it actually execute the thread method. Example 1 demonstrates creating and using a new thread.
Example 1. Spinning off a new thread
public class MyClass { public void ShowMessage( ) { Thread currentThread = Thread.CurrentThread; string caption = "Thread ID = "; caption += currentThread.ManagedThreadId; MessageBox.Show("ShowMessage runs on a new thread",caption); } } MyClass obj = new MyClass( );
ThreadStart threadStart = obj.ShowMessage; Thread workerThread = new Thread(threadStart); workerThread.Start( );
|
Calling the Start( )
method is a non-blocking operation, meaning that control returns
immediately to the client that started the thread, even though it may
be some time before the new thread actually starts (depending on the
operating system's internal threading management). As a result, after
calling Start( ), don't make any assumptions in your code that the thread is actually running.
Although you should have only one thread method as a target for the ThreadStart
delegate, you can associate it with multiple targets, in which case the
new thread executes all methods in order, and the thread terminates
once the last target method returns. However, there's little practical
use for such a setting. In general, you should have only one target
thread method. You can enforce that by passing the thread method name
directly to the Thread constructor, instead of using a delegate variable:
Thread workerThread = new Thread(obj.ShowMessage);
1.1. Designing thread methods
A
thread method can do whatever you want it to, but typically it will
contain a loop of some sort. In each loop iteration, the thread
performs a finite amount of work and then checks some condition, which
lets it know whether to perform another iteration or to terminate:
public void MyThreadMethod( )
{
while(<some condition>)
{
<Do some work>
}
}
If
the thread method completes its execution, .NET will shutdown the
thread gracefully. However, any unhandled exception thrown on the call
stack of the thread will terminate not just the thread but also will
trigger shutdown of hosting process itself. The one exception to this
rule is the ThreadAbortException (discussed later on), which
will only terminate the aborted thread. This behavior is a breaking
change introduced by .NET 2.0. In contract, .NET 1.1 will only
terminate the thread that encountered the unhandled exception. When
porting .NET 1.1 applications to .NET 2.0, make sure that your thread
methods always catch and handle any exception, otherwise you risk
unplanned application termination. |
The
condition is usually the result of some external event telling the
thread that its work is done—it can be as simple as checking the value
of a flag or waiting on a synchronization event. The condition is
usually changed by another thread. Consequently, changing and verifying
the condition must be done in a thread-safe manner, using threading
synchronization objects.