Using the Thread Pool for Asynchronous ProgrammingMany applications spend a lot of time doing nothing. Nothing, that is, except waiting for some kind of events or special circumstances to occur. After these events or circumstances have occurred, the application awakens, processes the events and then goes back to sleep. To make these tasks more efficient, .NET uses a thread pool. The following section explores the ThreadPool class and how to use it. Explaining the WaitCallback DelegateSimilar to the ThreadStart delegate, WaitCallback is a wrapper around the program code that is to be executed by a thread pool: WaitCallback workerThreadCallback = new WaitCallback(ThreadPoolWorkerThreadMethod); Unlike the THReadStart delegate, the WaitCallback delegate takes one parameter: static void ThreadPoolWorkerThreadMethod(Object stateObject) { ... } This parameter is a state object that can be used to pass information to the worker thread. Queuing a Work ItemIn order to use a thread pool, you have to queue work items. To do so, simply call the method THReadPool.QueueUserWorkItem and pass an instantiated WaitCallback delegate to this method: ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadPoolThreadMethod)); If you need to pass data or state information to a work item, use the overloaded form of the QueueWorkItem to pass in the extra information as an object: ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadPoolThreadMethod), stateInformation); Listing 9.9 demonstrates the use of a simple thread pool. It queues a work item and then waits for the user to press the Enter key before exiting the program. Because the thread pool uses background threads, removing the line of code that waits for the Enter key to be pressed would cause the program to exit before running the thread pool task. This is what is known as a race condition. TIP Just as bad as synchronization issues, race conditions can wreak havoc on your application. Any time you have a thread executing in the background, you need to make sure that one of two things is possible:
If the background thread fails to handle either of these two cases, your application will be unstable while that thread is running, and will produce inconsistent results every time the application terminates during that thread. Listing 9.9. Queuing a Work Itemusing System; using System.Threading; namespace SimpleThreadSample { public class SimpleThreadPool { [STAThread] static void Main(string[] args) { Console.WriteLine("{Main Thread} Queing the work item."); ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadPoolThreadMethod)); Console.WriteLine("{Main Thread} Press the 'Enter' key to exit the process."); Console.ReadLine(); Console.WriteLine("{Main Thread} Exiting the process."); } static void ThreadPoolThreadMethod(Object stateObject) { Console.WriteLine("{Thread Pool} Hello Thread Pool."); } } } Passing Data to ThreadsOccasionally, there is a need for a thread to use data or state from an outside section of code. You could use properties or methods to set the values of class-scope variables, but suppose that the thread method is a static method. If this is the case, the method does not have a pointer to the this variable and therefore does not have access to any class-scope variables. To solve this problem, the ThreadPool constructor takes a WaitCallback delegate as a parameter. As you learned earlier in this section, the WaitCallback delegate takes an object as its only parameter. This enables you to pass any information or state to the ThreadPool as an object that will in turn be passed to the thread method. Listing 9.10 demonstrates how to pass a variable to a ThreadPool worker thread. Listing 9.10. Passing Data to a Worker Threadusing System; using System.Threading; namespace SimpleThreadSample { public class SimpleThreadPool { [STAThread] static void Main(string[] args) { Console.WriteLine("{Main Thread} Queuing the work item."); ThreadPool.QueueUserWorkItem( ![]() |