Team LiB
Previous Section Next Section

Using the Thread Pool for Asynchronous Programming

Many 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 Delegate

Similar 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 Item

In 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 your application attempts to terminate, your thread will force the application to wait until the thread has completed its task successfully.

  • If the application terminates, your background thread process can trap the ThreadAbort exception and properly dispose of any in-progress work by rolling it back or whatever else needs to be done.

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 Item
using 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 Threads

Occasionally, 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 Thread
using 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( 
        new WaitCallback(ThreadPoolThreadMethod), "This is a state message");

      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} The data passed in is '" + stateObject.ToString() + "'");
    }
  }
}

    Team LiB
    Previous Section Next Section