This is due to how the thread scheduler works. It is struggling to ensure that it does not produce more pending threads than you have processor cores. It is a good idea to run more threads than cores is wasteful because Windows has a context for switching time between threads. Creating the total time needed to complete the job longer.
As soon as the TP thread completes, another one is allowed to start. Twice per second, the TP scheduler runs when threads are not completing. He cannot understand why these threads take so long to complete their work. Half a second is a lot of processor cycles, a cool billion or so. Therefore, it assumes that threads are blocked, waiting for any I / O to complete. Like a dbase request, reading a disk, trying to connect to a socket, like that.
And it allows you to start another thread. Now you have more threads, you have kernels. This is actually not a problem, if these source threads really block, they do not consume processor cycles.
You can see where this leads: if your thread runs for 3 seconds, then this creates a little trap. It delays but does not block other TP threads that are waiting to be launched. If your thread has to spend so much time because it is constantly blocked, you better create a regular thread. And if you're really curious that the thread is not delayed by the TP scheduler, you should also use Thread.
TP Scheduler has been redone in .NET 4.0. By the way, what I wrote is really true only for earlier releases. The basics still exist, it just uses a more reasonable scheduling algorithm. Feedback-based dynamic scheduling by measuring throughput. It really matters if you have many TP threads.
Hans passant
source share