Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the number of unused physical threads fluctuate in a .NET application?

I have a .NET application which I would expect to have 5 long-running threads operating including the main thread. I can see that indeed 4 threads are newed up across the codebase, and I believe there is no direct (e.g. work item queuing / tasks) or indirect (e.g. Timers) usage of the ThreadPool anywhere. At least none I can find.

Running the app under Performance Monitor shows that the number of recognized threads stays constant at 5 (as I would expect) but the number of physical threads fluctuates between 70 and 120 over the course of about an hour!

Does anyone know why there are so many unused (as far as I can tell) physical threads? And why this number fluctuates?

I can't find any documentation that would explain this behavior so my best guess is that the ThreadPool balances itself to accommodate changing environmental factors such as free memory and resource contention but the numbers here seem excessive.

Update

A senior support engineer at Microsoft confirmed that the physical thread counter in use definitely only reports threads for the current process, despite the odd wording in MSDN. If an answer suggests this is not the case it will need to point to a definitive source.

like image 1000
JRoughan Avatar asked Sep 08 '14 02:09

JRoughan


People also ask

How many threads should an application use?

Ideally, no I/O, synchronization, etc., and there's nothing else running, use 48 threads of task. Realistically, use about 95 threads may be better to exploit the max of your machine. Because: a core waits for data or I/O sometimes, so thread 2 could run while thread 1 not running.

What happens when there are too many threads?

Thus software threads tend to evict each other's data, and the cache fighting from too many threads can hurt performance. A similar overhead, at a different level, is thrashing virtual memory. Most computers use virtual memory. Virtual memory resides on disk, and the frequently used portions are kept in real memory.

How many CPU threads is too many?

Each core can only run 1 thread at a time, i.e. hyperthreading is disabled. So, you can have a total maximum of 20 threads executing in parallel, one thread per CPU/core. That can mean 20 single-threaded jobs, 1 multi-threaded job with 20 threads, or anything in between.

Does adding more threads continue to speed up the program do more threads ever slow down the program slightly Why or why not?

It is pretty straightforward and simple to understand. Having more threads than what your CPU supports you are actually serializing and not parallelizing. The more threads you have the slower your system will be.


4 Answers

Both ThreadPools and the GC create threads. There is a normal (or "worker") thread pool and an IO threadpool. The normal threadpool will allocate new threads as it feels it needs to to keep the threadpool responsive. It should create one thread per CPU right away, and probably one thread per second after that up to the minimum # of threads. See ThreadPool.GetMinThreads for the minimum number of worker threads the worker thread pool will create. See ThreadPool.GetAvailableThreads for the number of "active" worker threads in the worker thread pool. If you have long-running threads using worker thread-pool threads, this will make it think the thread is in use and allocate another to service future requests.

There is also a maximum # of threads in the pool, so as threads recycle back to the pool the pool may kill some off to get back down to a # it decides is best.

There is also a finalizer thread.

There are likely others that are undocumented or are a result of a library you're using.

Update:

I think part of the problem is confusion over "recognized threads" and "physical threads" and "unused threads". Recognized threads are documented as (emphasis mine)

These threads are associated with a corresponding managed thread object. The runtime does not create these threads, but they have run inside the runtime at least once.

Physical threads are documented as (emphasis mine)

native operating system threads created and owned by the common language runtime to act as underlying threads for managed thread objects

I'm guessing that the term "unused threads" by @JRoughan refers to "physical threads"--those that aren't "recognized". Which doesn't really mean they're unused, they're just not in the recognized counter. As the documentation points out, "physical threads" are created by the runtime, and I don't believe you can tell from either of those counters whether a thread is "used" or "unused"--depending on what @JRoughan means by "unused".

like image 117
Peter Ritchie Avatar answered Oct 23 '22 07:10

Peter Ritchie


Things like this do not have a simple answer. You need to investigate either under a debugger or using ETW traces.

With ETW traces, you can get events for each thread creation/destruction, optionally with call stack.

CLR itself could create threads for itself (e.g. GC threads, background GC threads, multicore JIT thread), thread pool threads, IO threads, timer thread. There is another kind of thread: gate thread.

Normally you can tell usage from the symbolic name of thread proc once symbols are resolved.

For ETW analysis, use PerfView from Microsoft.

like image 45
Bye StackOverflow Avatar answered Oct 23 '22 05:10

Bye StackOverflow


Is the application that you are testing in performance monitor a stantalone .net application or an application under IIS? If it is a stantalone application, probably you add some extra lib/code for using performace monitor. It mays create threads.

You can use Sysinternals' Process Explorer to watch threads in your process. You can see which method in which module started the threads.

enter image description here

like image 45
Sercan Avatar answered Oct 23 '22 05:10

Sercan


We can only speculate of course. My own bet would be about in-process COM servers. Those, and their associated threads, may be created when you use classes that wrap COM interfaces, such as the ones for directory services or WMI for example. Since they're created by native code (even though it's wrapped within a dotnet code), they're not recognized as managed threads.

like image 39
Victor Victis Avatar answered Oct 23 '22 07:10

Victor Victis