Thread BasicsA thread is a logical unit of execution for a program. The use of multiple threads in an application enables the programmer to create a more pleasurable experience for the user. Before getting into the details of how to employ the use of threads, you will learn some of the more important properties and methods of the thread class. In the following sections, you will learn how to create, run, join, stop, suspend, and pause a thread. Understanding the Key Thread Properties and MethodsThe thread class has several key properties. These properties enable you to control the basic makeup of a thread class. Table 9.1 is a list of some of the more important properties. TIP A background thread is essentially the same thing as a foreground thread, but with one exception: An active foreground thread prevents a process from closing, whereas a background thread does not. When a process terminates, it waits for all foreground threads to terminate and then issues an abort to all background threads. TIP Setting the name of a thread can be very useful when debugging a multithreaded application so that you can distinguish one thread from another. For example, if you issue Debug.WriteLine statements, providing the name of the thread from which the debug statement originated can be invaluable while stepping through an application. TIP Because this property has a Flags attribute, it can contain multiple values. To see whether a specific value is contained in this field, perform a bitwise comparison. Table 9.2 contains a listing of ThreadState values. In addition to these properties, several key methods are used to control the flow of threads. Those methods are listed here:
Explaining the ThreadStart DelegateA ThreadStart delegate is a wrapper around the program code that is to be executed by the thread. It is used as the only parameter passed during the creation of an instance of the Thread object. As with all delegates, it must contain the same parameters as the delegate declaration. You can create a ThreadStart delegate like this: ThreadStart workerThreadStart = new ThreadStart(SimpleWorkerThreadMethod); Creating a ThreadYou can use a Thread object to create a new instance of a thread to run a portion of the code associated with a process. However, before running a thread, you must first gain an instance of a Thread. Obtaining an instance of a thread can be accomplished in two ways. The first way is by creating a new instance of the thread class and passing in the ThreadStart delegate: Thread workerThread = new Thread(workerThreadStart); The second way is to use the static property CurrentThread from the thread class: Thread workerThread = Thread.CurrentThread; Running a ThreadRunning a thread is done by calling the Start method on an instance of a THRead object: workerThread.Start(); This causes the operating system to place the thread into a running state. When the thread is in a running state, the operating system can schedule it for execution. Terminating a ThreadThere are two ways that a thread can terminate. The first way is for the method that is wrapped by the ThreadStart delegate to finish its execution. This is demonstrated in the following code fragment: public static void SimpleWorkerThreadMethod() { Console.WriteLine("Hello from the worker thread."); } In this case, the thread will terminate immediately after the call to Console.WriteLine. The second case is when a thread receives a ThreadAbortException exception. In this case, a thread will always terminate. As you can see, there is an infinite loop in the following code sample: public static void SimpleWorkerThreadMethod() { for(int i=0; ; i++) { Console.WriteLine("Hello from the worker thread."); } } Under normal circumstances, this thread would never terminate. If you were to call this method from your main thread, the program would never terminate and you would have to kill the process. However, if you were to call this method from a separate thread, you could call the Thread.Abort method, which would cause the thread to terminate by throwing a THReadAbortException Listing 9.1. Sample Thread Applicationusing System; using System.Threading; namespace SimpleThreadSample { /// <summary> /// This class demonstrates the basic use of threads. /// </summary> class SimpleAbortThreadClass { [STAThread] static void Main(string[] args) { // Create the ThreadStart delegate. Console.WriteLine("{Main Thread} Creating the thread start delegate."); ThreadStart workerThreadStart = new ThreadStart(SimpleWorkerThreadMethod); // Pass the ThreadStart delegate to the Thread in the Thread constructor. Console.WriteLine("{Main Thread} Creating the worker thread."); Thread workerThread = new Thread(workerThreadStart); // Start the thread. Console.WriteLine("{Main Thread} Starting the worker thread."); workerThread.Start(); Console.ReadLine(); workerThread.Abort(); Console.WriteLine("{Main Thread} Aborting worker thread."); workerThread.Join(); Console.WriteLine("{Main Thread} Worker Thread Terminated."); Console.ReadLine(); } public static void SimpleWorkerThreadMethod() { for(int i=0; ; i++) { try { Console.WriteLine("Hello from the worker thread."); } Notice that in the SimpleWorkerThreadMethod method, the inside of the infinite loop is wrapped in an exception handler that catches all exceptions. This would typically catch any exception that occurs and suppress this error. But because the ThreadAbortException exception is a special case that cannot be suppressed, the exception is automatically rethrown after the exception handler. This causes the thread to terminate. The only way that this exception can be suppressed is to catch it and then call the Thread.AbortReset method. This is demonstrated by the following code sample: public static void SimpleWorkerThreadMethod() { for(int i=0; ; i++) { try { Console.WriteLine("Hello from the worker thread."); } catch(Exception e) { Console.WriteLine(e.ToString() + " Exception caught."); Thread.AbortReset(); } } } } } Suspending a ThreadWhen a thread is suspended, it is no longer scheduled by the processor for execution. It is simply waiting to be reawakened by some outside force. A thread can be suspended by simply calling the Thread.Suspend method. workerThread.Suspend(); When the Thread.Suspend method is called, the thread is suspended and no longer executing. To cause the thread to continue again, the Thread.Resume method must be called: workerThread.Resume(); Creating a Pause by "Sleeping" a ThreadWhen a thread is sleeping, it is simply blocked from execution for a specified period of time and is said to be in a WaitSleepJoin state. The WaitSleepJoin state will be explained in further detail in the next section. To place a thread in a sleeping state, simply call the Thread.Sleep method and pass the time for the thread to sleep, in milliseconds, to the method as its only parameter: workerThread.Sleep(1000); Passing in a 0 as the timeout causes the thread to be suspended, which allows other threads time to execute. Passing the keyword Infinite causes the thread to be blocked indefinitely. After the specified period of time, the thread is released and processing continues. Joining a ThreadCalling the Thread.Join method causes the calling thread to enter the WaitSleepJoin state and to be blocked until the thread instance (shown in the example that follows as workerThread) is terminated. If the thread does not terminate, the calling thread will be blocked indefinitely. To join a thread, simply call the Thread.Join method: workerThread.Join(); This method should be used when it is necessary to wait for a thread to terminate before continuing to process Listing 9.2. A Simple Thread Classusing System; using System.Threading; namespace SimpleThreadSample { /// <summary> // This class demonstrates the basic use of threads. /// </summary> class SimpleThreadClass { [STAThread] static void Main(string[] args) { // Create the ThreadStart delegate. Console.WriteLine("{Main Thread} Creating the thread start delegate."); ThreadStart workerThreadStart = new ThreadStart(SimpleWorkerThreadMethod); // Pass the ThreadStart delegate to the Thread in the Thread constructor. Console.WriteLine("{Main Thread} Creating the worker thread."); Thread workerThread = new Thread(workerThreadStart); |