From day 1, .NET presented an easy to use ThreadPool that will handle all of your queuing/dispatching needs.

While ThreadPool is a cool thing that saves you a lot of time dealing with multi threaded issues, one must be careful when using it and understand how it works and who else is using it to avoid common pitfalls.

Who Uses the ThreadPool?

  • Any asynchronous delegate invocation (BeginInvoke/EndInvoke pattern)
  • HttpWebRequest uses it internally to queue and dispatch web requests.
  • WebRequest uses it to dispatch the request
  • Some timers (like System.Threading.Timer).
  • The end user that uses Thread Pool directly.

The Problem

Consider a situation in which you have one of the above filling up the Thread Pool with queued items. Each queue item was implemeneted in a way that also uses the ThreadPool (either directly or by an async invocation). We then reach to a point where the ThreadPool is full and all of its requests are blocking and waiting to get some work done using the ThreadPool.

Bang! You are deadlocked! You have circular dependency that code that runs inside a ThreadPool worker thread is blocking because it runs a code that also needs the ThreadPool, but the ThreadPool is already full and it is also waiting to get a worker Thread to run on to of it.

Reaching this situation is quite easy under high load since by default, the ThreadPool is set to have 25 threads per CPU (A Hyper Threaded CPU is considered as two CPUs by .NET and the Operating System and therefore a ThreadPool running on that CPU will have 50 threads, 25 per CPU).

The Solution

There is actually more than one way of avoiding this situation:

  • Query the ThreadPool using ThreadPool.GetAvailableThreads to see if you should queue the item or not. If you can’t, either throw an exception or wait until you can use it (just don’t wait if you are on a Thread Pool worker thread, i.e. you are running inside an ASP.NET page 😉 We are trying to avoid that). This is a similar approach that .NET takes internally.
  • Avoidance – Don’t call this code from within a place that uses the ThreadPool such as from within ASP.NET or from within an Async call.
  • Implement your own ThreadPool instead of using .NET’s ThreadPool – This is not as easy as it sounds, though.

There is an additional solution but its ONLY applicable in the coming .NET 2.0 which is to specifically call ThreadPool.SetMaxThreads. By doing so you will be able to increase the amount of threads available to the ThreadPool.

NOTE: Do mind that having too many threads per CPU is not recommended since the Operating System will spend a lot of time just making sure they are not starved.

As we can see, ThreadPool is a great thing that saves us the time and effort of implementing a multi threaded pool with all of its synchronization and other thread related issues but we still must understand that it has its limitation and we need to understand how it works and what it do to avoid unnecessary deadlocks and/or strange behavior.