Thread Pooling

I鈥檒l close this chapter by briefly mentioning a handy System.Threading class named ThreadPool that provides a managed thread pooling API. The basic idea behind thread pooling is that instead of launching threads yourself, you pass requests to a thread pool manager. The thread pool manager maintains a pool of threads that鈥檚 sized as needed to satisfy incoming requests. The following example demonstrates a very simple use of the ThreadPool class:

using聽System;
using聽System.Threading;

class聽MyApp
{
聽聽聽聽static聽int聽count聽=聽0;

聽聽聽聽static聽void聽Main聽()
聽聽聽聽{
聽聽聽聽聽聽聽聽WaitCallback聽callback聽=聽new聽WaitCallback聽(ProcessRequest);

聽聽聽聽聽聽聽聽ThreadPool.QueueUserWorkItem聽(callback);
聽聽聽聽聽聽聽聽ThreadPool.QueueUserWorkItem聽(callback);
聽聽聽聽聽聽聽聽ThreadPool.QueueUserWorkItem聽(callback);
聽聽聽聽聽聽聽聽ThreadPool.QueueUserWorkItem聽(callback);
聽聽聽聽聽聽聽聽ThreadPool.QueueUserWorkItem聽(callback);
聽聽聽聽聽聽聽聽ThreadPool.QueueUserWorkItem聽(callback);
聽聽聽聽聽聽聽聽ThreadPool.QueueUserWorkItem聽(callback);
聽聽聽聽聽聽聽聽ThreadPool.QueueUserWorkItem聽(callback);
聽聽聽聽聽聽聽聽ThreadPool.QueueUserWorkItem聽(callback);
聽聽聽聽聽聽聽聽ThreadPool.QueueUserWorkItem聽(callback);

聽聽聽聽聽聽聽聽Thread.Sleep聽(5000);聽//聽Give聽the聽requests聽a聽chance聽to聽execute
聽聽聽聽}

聽聽聽聽static聽void聽ProcessRequest聽(object聽state)
聽聽聽聽{
聽聽聽聽聽聽聽聽int聽n聽=聽Interlocked.Increment聽(ref聽count);聽聽聽聽聽聽聽聽
聽聽聽聽聽聽聽聽Console.WriteLine聽(n);
聽聽聽聽}
}

This application counts from 1 to 10 in a console window and uses background threads to do the writing. Rather than launch 10 threads itself, the application submits 10 requests to ThreadPool by calling ThreadPool.QueueUserWorkItem. Then ThreadPool determines how many threads are needed to handle the requests.

You can use an alternate form of QueueUserWorkItem鈥?/span>one that accepts an Object in its second parameter鈥攖o pass additional information in each request. The following example passes an array of five integers:

int[]聽vals聽=聽new聽int[5]聽{聽1,聽2,聽3,聽4,聽5聽};
ThreadPool.QueueUserWorkItem聽(callback,聽vals);

QueueUserWorkItem鈥檚 second parameter is the first (and only) parameter to the callback method:

static聽void聽ProcessRequest聽(object聽state)
{
聽聽聽聽int[]聽vals聽=聽(int[])聽state;
聽聽聽聽聽聽...
}

A simple cast converts the Object reference to a strong type and gives the callback method access to the data provided by the requestor.

You must never terminate a pooled thread. The thread pool manager creates pooled threads, and it terminates them, too. You can use Thread鈥檚 IsThreadPoolThread property to determine whether a thread is a pooled thread. In the following example, the current thread terminates itself if and only if it鈥檚 a nonpooled thread:

if聽(!Thread.CurrentThread.IsThreadPoolThread)
聽聽聽聽Thread.CurrentThread.Abort聽();

For server applications anticipating a high volume of requests that require concurrent processing, thread pooling simplifies thread management and increases performance. The performance increase comes because the thread pool manager maintains a pool of threads that it can use to service requests. It鈥檚 far faster to transfer a call to an existing thread than it is to launch a whole new thread from scratch. Plus, divvying up requests among threads enables the system to take advantage of multiple CPUs if they鈥檙e present. You do the easy part by handing requests off to ThreadPool. ThreadPool does the rest.