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?
Code:

Output:

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?
Code:


Output:

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?
Code:

Output

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?
Code:

Output

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
<configuration>
<system.net>
<connectionManagement>
<add address="*" maxconnection="24" />
</connectionManagement>
</system.net>
<system.web>
<processModel
autoConfig="true"
maxWorkerThreads = "100"
maxIoThreads
= "100"
minWorkerThreads = "50"
minIoThreads = "50"
/>
<httpRuntime
minFreeThreads="176"
minLocalRequestFreeThreads="152"
/>
</system.web>
</configuration>
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