We all do it. We end up using the ThreadPool either directly or when invoking a delegate asynchronously.
A few days ago, while testing a patch that makes monologue download all the sindicated files asynchronously we found what appeared to be a bug but is in turn a feature.
Let's see some code that shows up the problem:
using System.Threading;
class TPTest
{
static int count;
delegate void DummyDelegate ();
static void Main ()
{
for (int i = 0; i < 50; i++)
ThreadPool.QueueUserWorkItem (new WaitCallback (Begin));
while (true) {
Thread.Sleep (500);
int done = Interlocked.CompareExchange (ref count, 0, -1);
Console.WriteLine ("{0} finished.", done);
if (done == 50) {
Console.WriteLine ("DONE!");
break;
}
}
}
static void Begin (object unused)
{
DummyDelegate d = new DummyDelegate (SomethingElse);
IAsyncResult ares = d.BeginInvoke (null, null);
d.EndInvoke (ares);
Interlocked.Increment (ref count);
}
static void SomethingElse ()
{
Thread.Sleep (5000);
}
}
It doesn't look like something that could hang... until you run it.
What happens in there?
- The for loop adds 50 tasks to the ThreadPool.
- Each of those tasks create a delegate and run BeginInvoke.
- BeginInvoke also uses the ThreadPool so more tasks are queued.
- EndInvoke waits indefinitely
The default number of threads in the ThreadPool is 25 per processor. You can check this out with:
ThreadPool.GetMaxThreads (out max_threads, out max_completion_ports);
The tasks queued in step 3 are never executed because the ThreadPool queue is already full since step 1. This is what makes EndInvoke wait indefinitely, which in turn keeps the thread busy thus preventing the queue from shrinking... We got a deadlock!
If you want to see the number of available threads in the ThreadPool going down, just add these 3 lines before BeginInvoke:
ThreadPool.GetAvailableThreads (out threads, out completion_ports);
Console.WriteLine ("Available threads: {0}", threads);
The conclusion is that we should avoid waiting for asynchronous calls in a thread belonging to the ThreadPool. Or at least do it discretely...
blog comments powered by Disqus