I will try to answer following questions in this article:
- How many threads are available in .NET thread pool?
- How long does the CLR take to create a new Non-thread-pool thread?
- How long does the CLR take to schedule a task on a thread-pool thread?
- How long does the CLR take to create a new thread-pool thread when it does not have enough thread available in the pool?
How many threads are available in .NET thread pool?
The min number of workerThreads is 8. It is the number of threads available in the tread pool when you start a program. I will prove it with my testing later in this article.
How long does the CLR take to create a new Non-thread pool thread?
In the test, I simple create 100 CRL worker thread, in each tread, it just sleeps for 5 seconds and increase a counter. When counter values reaches to 100, means all threads completed, then the test stops.
The result shows it took 220 milliseconds to create all the threads, so in average, .NET takes about 2.2 milliseconds to create a worker thread.
The whole test took 5252 milliseconds to complete, as each thread sleeps for 5 seconds, and all thread run in parallel. So the numbers all add up.
Note: 100 threads is a very small sample size, and how long exactly .NET will take to create a thread will vary on different hardware/test environment. The purpose of this article is just to give you some basic ideas of how expensive some operations are, rather than the accuracy. This will apply for the rest of the article.
How long does the CLR take to schedule a task on a thread-pool thread?
Again, similarly test, just use thread-pool threads rather than creating our own threads this time. The result shows now it only takes 0.147 milliseconds for each thread to be scheduled, much faster compare to 2.2 milliseconds in last test. Just as expected, right? So far so good, or maybe, so far so boring…
Now, here comes the interesting part, this is the result for total amount of time to complete the test:
What?! It is much longer than the last test! Why?!
To show what’s going on, I have printed the thread ID in each worker thread. Here is what it looks like:
The first 8 threads were very quickly printed on the screen, pretty much at same time. Then starts from the 9th, the next 8 threads were pretty slow, each one took about 1 second to show on the screen. Then the most of the rest became very fast again, with just a few slower exceptions.
Here is the explanation: As we have showed in the beginning of this article, the min number of workerThreads is 8, and it must be the number of threads available in the thread pool when a program starts. That explains why the first 8 threads started running very fast. Then as the program requires more threads, and thread pool become empty, it starts to create new threads, so the next 8 threads started very slowly. It took about 0.5-1 seconds to create a thread, after the next 8 threads started which took about 5 seconds, and then the previously scheduled threads began to return back to the pool, starting new jobs became fast again until the thread pool is running out.
To prove this, I changed the min worker thread settings and ran the same test again:
The total time spent is just over 5 seconds now, no more time spent on creating new thread-pool thread.
Now we want to find out:
How long does the CLR take to create a new thread-pool thread when it does not have enough thread available in the pool?
Now, we want to find out the time .NET will take to create new thread-pool thread. So we can’t let the worker thread sleep anymore. I used ManualResetEvent to signal the worker threads to return. Hopefully, ManualResetEvent will take no time to trigger the return. But again, accuracy is not a big concern here.
The result shows it took 951.57 milliseconds to create a thread-pool thread.
Now, here comes a question, why it takes so long to create a thread-pool thread? My previous test has just showed it only takes 2.2 milliseconds to create a CLR worker thread? Why thread pool takes so long?
It turns out that the time it spends not only for creating a thread, rather there is some logic for optimising thread pool. It will make decision to create a new thread or wait for a thread to be available. This is from MSDN:
When a minimum is reached, the thread pool can create additional threads or wait until some tasks complete. Beginning with the .NET Framework 4, the thread pool creates and destroys worker threads in order to optimize throughput, which is defined as the number of tasks that complete per unit of time. Too few threads might not make optimal use of available resources, whereas too many threads could increase resource contention.
As most of the MSDN documents, it is not that clear on what it is trying to say. But I think it can be translated into simple English as
“When .NET thread pool is empty, it will only create a new thread for about every 0.5-1 seconds. “
Change machine.config for larger number of minWorkerthread
So for a program, if we know we are going to use a lot of threads from thread-pool, e.g. WCF service hosted by IIS, we better have larger number minWorkerThread. We can do this by changing machine.config. (%windir%\Microsoft.NET\Framework64\[version]\config\machine.config)
Here is an example
<add address="*" maxconnection="24" />
maxWorkerThreads = "100"
maxIoThreads = "100"
minWorkerThreads = "50"
minIoThreads = "50"
Note: the thread numbers in the config are per CPU. minWorkerThreads = “50”, will let me to have 400 threads in the thread pool as my machine has 8 cores. That also explains the magic default minWorkerThreads value “8” (not Chinese lucky number!).
Here is an article about more details of Tuning IIS – machine.config settings