For your first multithreaded application, you will learn the basic techniques of working with the scheduler. This means that you will learn the syntax to create, terminate, suspend, sleep, and join threads, as well as what the uses are for each of those operations.
Creating and Running a Thread
When creating a thread, you need to use a special delegate called ThreadStart. This delegate will contain a reference to the method that contains the work you want to be performed in the thread. As explained in Chapter 9, delegates are really nothing more than special types that contain methods with specific signatures. You can invoke delegates the same way you invoke methods, and you can pass delegates as parameters to other methods, which is exactly what needs to be done when creating a thread.
To create a thread, you pass a ThreadStart delegate to the constructor of the Thread class, as shown in the following example:
ThreadStart myThreadDelegate = new ThreadStart(MyWork); Thread workerThread = new Thread(myThreadDelegate);
What you will often see in many code samples is the preceding two lines of code consolidated into the following:
Thread workerThread = new Thread(new ThreadStart(MyWork));
In this line of code, MyWork is the name of the method that matches the signature defined by the ThreadStart delegate. All ThreadStart delegates must be void methods.
The code in Listing 10.1 shows how to create and start a thread, as well as how to continuously poll the status of a thread as a crude way of checking to see if it's finished. You'll see a more elegant solution later.
Listing 10.1. Starting and Running a Thread
Figure 10.1 shows the output of this sample.
Figure 10.1. Starting and running a threadsample output.
Terminating a Thread
Terminating a thread involves using the Abort method on the thread. Abort can either be called on the thread instance by the block of code that initially created it, or it can be called by the running thread itself.
When a thread is aborted, a ThreadAbortException is thrown. As you will find out in various samples throughout this book, you can trap and suppress most exceptions. The ThreadAbortException is the one exception in the .NET Framework that cannot be ignoredwith good reason. This exception must be allowed to travel up the chain of exceptions in order for aborted threads to know to stop working. You can manually suppress this exception with the Thread.ResetAbort() method, as shown in Listing 10.2, which illustrates aborting a running thread.
Listing 10.2. Aborting a Running Thread
Figure 10.2 shows a screenshot of the console output of this program. As soon as the user presses Enter, the thread abort signal is sent, and the worker thread catches (and suppresses) the ThreadAbortException exception. If the worker method didn't suppress this, the exception would "bubble up" and eventually cause the main application to stoptypically an undesired result.
Figure 10.2. Thread abort demo output.
Suspending a Thread
When you suspend a thread, you tell the scheduler that the thread no longer needs to be swapped to the foreground for execution. What this means is that as soon as the thread stops executing to give time to another thread, the thread will not continue until it has been resumed.
You suspend a thread with the Suspend method. It takes no arguments and works fairly simply. To resume the thread at will, you can simply call the Resume method on that same thread.
Sleeping a Thread
You saw in Listing 10.2 that there is a method called Sleep that does exactly what it sounds like: causes the thread to sleep. By supplying a time interval in milliseconds, the thread will stop executing at that line for the specified duration. You can also pass a 0 as the argument, which will cause the thread to be suspended. If you specify System.Threading.Timeout.Infinite as the value, the thread will block indefinitely.
Joining a Thread
The Join method serves as a way to block the current thread until the specified thread has completed. This essentially allows the thread to wait for the completion of another method. This is where the term join comes from, where the current thread will wait for another thread to "catch up." Listing 10.3 illustrates the use of the Join method.
Listing 10.3. Joining a Thread
The use of Join replaces the loop seen in an earlier example where the code executed a while loop that continuously looped until the IsAlive property of the executing thread was false. As mentioned earlier, using Join is a far more elegant (and thread-safe) solution.