© 2006-2010 Joseph Albahari, O’Reilly Media, Inc. All rights reserved. www.albahari.com/threading/
18
(These figures may vary according to the hardware and operating system.) The reason there are that many is to ensure
progress should some threads be blocked (idling while awaiting some condition, such as a response from a remote
computer).
You can also set a lower limit by calling
ThreadPool.SetMinThreads
. The role of the lower limit is subtler: it’s an
advanced optimization technique that instructs the pool manager not to delay in the allocation of threads until reaching
the lower limit. Raising the minimum thread count improves concurrency when there are blocked threads (see sidebar).
The default lower limit is one thread per processor core—the minimum that allows full CPU utilization. On
server environments, though (such ASP.NET under IIS), the lower limit is typically much higher—as much as
50 or more.
How Does the Minimum Thread Count Work?
Increasing the thread pool’s minimum thread count to x doesn’t actually force x threads to be created right
away—threads are created only on demand. Rather, it instructs the pool manager to create up to x threads the
instant they are required. The question, then, is why would the thread pool otherwise delay in creating a thread
when it’s needed?
The answer is to prevent a brief burst of short-lived activity from causing a full allocation of threads, suddenly
swelling an application’s memory footprint. To illustrate, consider a quad-core computer running a client
application that enqueues 40 tasks at once. If each task performs a 10 ms calculation, the whole thing will be
over in 100 ms, assuming the work is divided among the four cores. Ideally, we’d want the 40 tasks to run on
exactly four threads:
• Any less and we’d not be making maximum use of all four cores.
• Any more and we’d be wasting memory and CPU time creating unnecessary threads.
And this is exactly how the thread pool works. Matching the thread count to the core count allows a program to
retain a small memory footprint without hurting performance—as long as the threads are efficiently used
(which in this case they are).
But now suppose that instead of working for 10 ms, each task queries the Internet, waiting half a second for a
response while the local CPU is idle. The pool manager’s thread-economy strategy breaks down; it would now
do better to create more threads, so all the Internet queries could happen simultaneously.
Fortunately, the pool manager has a backup plan. If its queue remains stationary for more than half a second, it
responds by creating more threads—one every half-second—up to the capacity of the thread pool.
The half-second delay is a two-edged sword. On the one hand, it means that a one-off burst of brief activity
doesn’t make a program suddenly consume an extra unnecessary 40 MB (or more) of memory. On the other
hand, it can needlessly delay things when a pooled thread blocks, such as when querying a database or calling
WebClient.DownloadFile
. For this reason, you can tell the pool manager not to delay in the allocation of the
first x threads, by calling
SetMinThreads
, for instance:
ThreadPool.SetMinThreads (50, 50);
(The second value indicates how many threads to assign to I/O completion ports, which are used by the APM,
described in Chapter 23 of C# 4.0 in a Nutshell.)
The default value is one thread per core.