Question Threadpool

DrDress

New member
Joined
Aug 17, 2021
Messages
3
Programming Experience
5-10
I'm not sure about Threadpool. It says thar there are about 1000 available threads. But I once made a program where I put about 100 job in the queue. When I monitored the job it was clear that precisly 8 jobs whete beeing processed at a time. This is bad in my current project where I spawn several threads and many of them cannot nescesarily wait if all 8 threads are being used for several seconds.

Is this always the case? Or is it computer demendant?

Is there a way control or monitor this via some internal command? Or any other input?
 
There may be 1000 available threads, but there is a set number of cores available on your CPU to able to run those threads. The idea of having a thread pool is that it is expensive to create and start up a new thread, but it's not quite as expensive to switch between threads that have already been created. The thread pool is a collection of those pre-created threads.
 
Ok that makes sense. But if I put many jobs in the pool it didn't seem to switch between them all, but only a few at a time. Do you know if something like is the case?
 
Yes, that is normal.
 
Did you read the documentation for the ThreadPool class? We can help with stuff that you can't work out for yourself but you should always try to work it our for yourself and reading the documentation for the types and/or members you want to use is ALWAYS one of the first steps in doing that. The documentation for the ThreadPool class will tell you how it works, what you can control and how to do so.

Given that you can simply click a type or member in the VS code editor and press F1 to do straight to the relevant documentation, there's no good reason to not have done so. You should also have the documentation home page favourited/bookmarked in your browser so that you can search for what you want, when you want. You should read about the ThreadPool class and its members now and you should find some useful information.
 
+1 @jmcilhinney regarding checking the documentation first!

Unfortunately, the ThreadPool documentation doesn't make mention of how work that has been queued to be run by the thread pool is scheduled to be executed. On one hand it shouldn't have to because it has declared multiple times that work queued there is meant to be background operations, and it even explicitly lists what should and should not be done with thread pools. Furthermore, thread scheduling is also OS dependent and may not fully be under the control of the framework. It is not the job of the framework documentation to teach OS concepts. On the other hand, the documentation doesn't hint that scheduling the work items may not run in parallel with other scheduled work items.
 
The Remarks section of main topic the the class has relevant information. In short, a small number of threads will be used initially and that number will grow if appropriate. Basically, the ThreadPool is supposed to be set and forget and let the system manage it. If you have specific needs then the ThreadPool is probably not for you.

That said, even high-end CPUs these days will only have 8 physical cores so there's only so much work that can actually be done in real parallel. If you create 1000 threads then they are going to have to be interleaved anyway so, unless the work they are doing has natural pauses in it, you're not actually gaining anything by forcing everything to happen as though it's at the same time. You can't do any more work than the available processor time allows and you have the overhead of switching between threads. That's why it is generally more efficient to run a small number of tasks to completion and then reuse the same threads for another set of a small number of tasks. The ThreadPool is designed the way it is because that's the way that is usually most efficient.
 
The Remarks section of main topic the the class has relevant information. In short, a small number of threads will be used initially and that number will grow if appropriate. Basically, the ThreadPool is supposed to be set and forget and let the system manage it. If you have specific needs then the ThreadPool is probably not for you.
How did you get that from?
There is one thread pool per process. Beginning with the .NET Framework 4, the default size of the thread pool for a process depends on several factors, such as the size of the virtual address space. A process can call the GetMaxThreads method to determine the number of threads. The number of threads in the thread pool can be changed by using the SetMaxThreads method. Each thread uses the default stack size and runs at the default priority.
:
The thread pool provides new worker threads or I/O completion threads on demand until it reaches the maximum for each category. When a maximum is reached, the thread pool can create additional threads in that category 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.
given that the OP claims that the ThreadPool was saying that it had 1000 available threads?
 
Hi again

Thanks for all the great input. And, yes i did read the documentation and tried some tests in the code. So I might have to rethink using thread pool if I have a lot of jobs which have to be scheduled in equally.

Maybe I have some mutex deadlock somewhere in the code, too. That would exasperate the issue.
 
Here's how the ThreadPool works. It starts off with the minimum number of threads actually running. By default, that number is 8. When you start queuing tasks, they get allocated to run on those initial threads. Once the number of queued tasks is greater than the number of existing threads, new threads are created, but only at a rate of one per second. This creation of a new thread once per second will continue as long as there are more tasks queued than threads running. As tasks complete, the threads they were running on will be reallocated to remaining queued tasks as required, reducing the need to create further new threads. Once there no more tasks queued, existing threads will exit until the number is down to the minimum again. To see that in action, try running this code:
C#:
private void button1_Click(object sender, EventArgs e)
{
    for (int i = 0; i < 100; i++)
    {
        ThreadPool.QueueUserWorkItem(DoWork);
    }
}

private void DoWork(object dummy)
{
    for (int i = 0; i < 3; i++)
    {
        ThreadPool.GetMaxThreads(out var maxWorkerThreads, out _);
        ThreadPool.GetAvailableThreads(out var availableWorkerThreads, out _);
        Console.WriteLine($"{DateTime.Now.TimeOfDay}: Max = {maxWorkerThreads}; Available = {availableWorkerThreads}; Allocated = {maxWorkerThreads - availableWorkerThreads}");
        Thread.Sleep(10000);
    }
}
Here's the output when I ran it:
16:21:40.8611267: Max = 2047; Available = 2039; Allocated = 8
16:21:40.8621261: Max = 2047; Available = 2039; Allocated = 8
16:21:40.8621261: Max = 2047; Available = 2039; Allocated = 8
16:21:40.8621261: Max = 2047; Available = 2039; Allocated = 8
16:21:40.8621261: Max = 2047; Available = 2039; Allocated = 8
16:21:40.8621261: Max = 2047; Available = 2039; Allocated = 8
16:21:40.8611267: Max = 2047; Available = 2039; Allocated = 8
16:21:40.8661508: Max = 2047; Available = 2039; Allocated = 8
16:21:41.8518840: Max = 2047; Available = 2038; Allocated = 9
16:21:42.8388140: Max = 2047; Available = 2037; Allocated = 10
16:21:43.8467191: Max = 2047; Available = 2036; Allocated = 11
16:21:44.8533565: Max = 2047; Available = 2035; Allocated = 12
16:21:45.8410587: Max = 2047; Available = 2034; Allocated = 13
16:21:46.8447021: Max = 2047; Available = 2033; Allocated = 14
16:21:47.8543395: Max = 2047; Available = 2032; Allocated = 15
16:21:48.8490491: Max = 2047; Available = 2031; Allocated = 16
16:21:49.8534486: Max = 2047; Available = 2030; Allocated = 17
16:21:50.8448659: Max = 2047; Available = 2029; Allocated = 18
16:21:50.8658689: Max = 2047; Available = 2029; Allocated = 18
16:21:50.8658689: Max = 2047; Available = 2029; Allocated = 18
16:21:50.8658689: Max = 2047; Available = 2029; Allocated = 18
16:21:50.8658689: Max = 2047; Available = 2029; Allocated = 18
16:21:50.8658689: Max = 2047; Available = 2029; Allocated = 18
16:21:50.8658689: Max = 2047; Available = 2029; Allocated = 18
16:21:50.8658689: Max = 2047; Available = 2029; Allocated = 18
16:21:50.8808799: Max = 2047; Available = 2029; Allocated = 18
16:21:51.8516694: Max = 2047; Available = 2028; Allocated = 19
16:21:51.8557066: Max = 2047; Available = 2028; Allocated = 19
16:21:52.8392910: Max = 2047; Available = 2027; Allocated = 20
16:21:52.8494788: Max = 2047; Available = 2027; Allocated = 20
16:21:53.8454057: Max = 2047; Available = 2026; Allocated = 21
16:21:53.8564088: Max = 2047; Available = 2026; Allocated = 21
16:21:54.8548714: Max = 2047; Available = 2025; Allocated = 22
16:21:54.8590271: Max = 2047; Available = 2025; Allocated = 22
16:21:55.8480003: Max = 2047; Available = 2025; Allocated = 22
16:21:55.8588900: Max = 2047; Available = 2024; Allocated = 23
16:21:56.8393154: Max = 2047; Available = 2023; Allocated = 24
16:21:56.8483442: Max = 2047; Available = 2023; Allocated = 24
16:21:57.8492763: Max = 2047; Available = 2022; Allocated = 25
16:21:57.8692814: Max = 2047; Available = 2022; Allocated = 25
16:21:58.8580611: Max = 2047; Available = 2021; Allocated = 26
16:21:58.8580611: Max = 2047; Available = 2021; Allocated = 26
16:21:59.8409686: Max = 2047; Available = 2020; Allocated = 27
16:21:59.8652092: Max = 2047; Available = 2020; Allocated = 27
16:22:00.8526269: Max = 2047; Available = 2019; Allocated = 28
16:22:00.8536271: Max = 2047; Available = 2019; Allocated = 28
16:22:00.8764692: Max = 2047; Available = 2019; Allocated = 28
16:22:00.8764692: Max = 2047; Available = 2019; Allocated = 28
16:22:00.8764692: Max = 2047; Available = 2019; Allocated = 28
16:22:00.8764692: Max = 2047; Available = 2019; Allocated = 28
16:22:00.8764692: Max = 2047; Available = 2019; Allocated = 28
16:22:00.8764692: Max = 2047; Available = 2019; Allocated = 28
16:22:00.8764692: Max = 2047; Available = 2019; Allocated = 28
16:22:00.8914891: Max = 2047; Available = 2019; Allocated = 28
16:22:01.8554850: Max = 2047; Available = 2018; Allocated = 29
16:22:01.8554850: Max = 2047; Available = 2018; Allocated = 29
16:22:01.8635389: Max = 2047; Available = 2018; Allocated = 29
16:22:02.8435367: Max = 2047; Available = 2018; Allocated = 29
16:22:02.8555212: Max = 2047; Available = 2017; Allocated = 30
16:22:02.8565069: Max = 2047; Available = 2017; Allocated = 30
16:22:03.8443188: Max = 2047; Available = 2016; Allocated = 31
16:22:03.8495768: Max = 2047; Available = 2016; Allocated = 31
16:22:03.8651488: Max = 2047; Available = 2016; Allocated = 31
16:22:04.8487768: Max = 2047; Available = 2015; Allocated = 32
16:22:04.8657970: Max = 2047; Available = 2015; Allocated = 32
16:22:04.8657970: Max = 2047; Available = 2015; Allocated = 32
16:22:05.8470107: Max = 2047; Available = 2014; Allocated = 33
16:22:05.8490190: Max = 2047; Available = 2014; Allocated = 33
16:22:05.8650960: Max = 2047; Available = 2014; Allocated = 33
16:22:06.8414363: Max = 2047; Available = 2014; Allocated = 33
16:22:06.8505526: Max = 2047; Available = 2013; Allocated = 34
16:22:06.8505526: Max = 2047; Available = 2013; Allocated = 34
16:22:07.8710937: Max = 2047; Available = 2012; Allocated = 35
16:22:07.8710937: Max = 2047; Available = 2012; Allocated = 35
16:22:07.8730917: Max = 2047; Available = 2012; Allocated = 35
16:22:08.8448435: Max = 2047; Available = 2011; Allocated = 36
16:22:08.8641865: Max = 2047; Available = 2011; Allocated = 36
16:22:08.8641865: Max = 2047; Available = 2011; Allocated = 36
16:22:09.8487639: Max = 2047; Available = 2011; Allocated = 36
16:22:09.8627100: Max = 2047; Available = 2010; Allocated = 37
16:22:09.8794121: Max = 2047; Available = 2010; Allocated = 37
16:22:10.8471398: Max = 2047; Available = 2009; Allocated = 38
16:22:10.8676280: Max = 2047; Available = 2009; Allocated = 38
16:22:10.8676280: Max = 2047; Available = 2009; Allocated = 38
16:22:10.8826809: Max = 2047; Available = 2009; Allocated = 38
16:22:10.8826809: Max = 2047; Available = 2009; Allocated = 38
16:22:10.8826809: Max = 2047; Available = 2009; Allocated = 38
16:22:10.8826809: Max = 2047; Available = 2009; Allocated = 38
16:22:10.8826809: Max = 2047; Available = 2009; Allocated = 38
16:22:10.8826809: Max = 2047; Available = 2009; Allocated = 38
16:22:10.8826809: Max = 2047; Available = 2009; Allocated = 38
16:22:10.8978268: Max = 2047; Available = 2009; Allocated = 38
16:22:11.8388087: Max = 2047; Available = 2008; Allocated = 39
16:22:11.8655382: Max = 2047; Available = 2008; Allocated = 39
16:22:11.8655382: Max = 2047; Available = 2008; Allocated = 39
16:22:11.8655382: Max = 2047; Available = 2008; Allocated = 39
16:22:12.8452601: Max = 2047; Available = 2007; Allocated = 40
16:22:12.8452601: Max = 2047; Available = 2007; Allocated = 40
16:22:12.8564664: Max = 2047; Available = 2007; Allocated = 40
16:22:12.8718855: Max = 2047; Available = 2007; Allocated = 40
16:22:13.8503318: Max = 2047; Available = 2006; Allocated = 41
16:22:13.8503318: Max = 2047; Available = 2006; Allocated = 41
16:22:13.8643216: Max = 2047; Available = 2006; Allocated = 41
16:22:13.8794086: Max = 2047; Available = 2006; Allocated = 41
16:22:14.8481259: Max = 2047; Available = 2005; Allocated = 42
16:22:14.8636719: Max = 2047; Available = 2005; Allocated = 42
16:22:14.8795645: Max = 2047; Available = 2005; Allocated = 42
16:22:14.8795645: Max = 2047; Available = 2005; Allocated = 42
16:22:15.8450846: Max = 2047; Available = 2004; Allocated = 43
16:22:15.8511205: Max = 2047; Available = 2004; Allocated = 43
16:22:15.8511205: Max = 2047; Available = 2004; Allocated = 43
16:22:15.8660912: Max = 2047; Available = 2004; Allocated = 43
16:22:16.8430714: Max = 2047; Available = 2004; Allocated = 43
16:22:16.8530294: Max = 2047; Available = 2003; Allocated = 44
16:22:16.8530294: Max = 2047; Available = 2003; Allocated = 44
16:22:16.8530294: Max = 2047; Available = 2003; Allocated = 44
16:22:17.8530085: Max = 2047; Available = 2002; Allocated = 45
16:22:17.8740102: Max = 2047; Available = 2002; Allocated = 45
16:22:17.8740102: Max = 2047; Available = 2002; Allocated = 45
16:22:17.8740102: Max = 2047; Available = 2002; Allocated = 45
16:22:18.8408614: Max = 2047; Available = 2001; Allocated = 46
16:22:18.8499573: Max = 2047; Available = 2001; Allocated = 46
16:22:18.8659903: Max = 2047; Available = 2001; Allocated = 46
16:22:18.8659903: Max = 2047; Available = 2001; Allocated = 46
16:22:19.8537410: Max = 2047; Available = 2000; Allocated = 47
16:22:19.8537410: Max = 2047; Available = 2000; Allocated = 47
16:22:19.8743639: Max = 2047; Available = 2000; Allocated = 47
16:22:19.8899460: Max = 2047; Available = 2000; Allocated = 47
16:22:20.8490214: Max = 2047; Available = 2000; Allocated = 47
16:22:20.8520226: Max = 2047; Available = 1999; Allocated = 48
16:22:20.8793452: Max = 2047; Available = 1999; Allocated = 48
16:22:20.8793452: Max = 2047; Available = 1999; Allocated = 48
16:22:20.8947426: Max = 2047; Available = 1999; Allocated = 48
16:22:20.8947426: Max = 2047; Available = 1999; Allocated = 48
16:22:20.8947426: Max = 2047; Available = 1999; Allocated = 48
16:22:20.8947426: Max = 2047; Available = 1999; Allocated = 48
16:22:20.8947426: Max = 2047; Available = 1999; Allocated = 48
16:22:20.8947426: Max = 2047; Available = 1999; Allocated = 48
16:22:20.8947426: Max = 2047; Available = 1999; Allocated = 48
16:22:20.9097441: Max = 2047; Available = 1999; Allocated = 48
16:22:21.8523396: Max = 2047; Available = 1998; Allocated = 49
16:22:21.8533382: Max = 2047; Available = 1998; Allocated = 49
16:22:21.8813373: Max = 2047; Available = 1998; Allocated = 49
16:22:21.8813373: Max = 2047; Available = 1998; Allocated = 49
16:22:21.8813373: Max = 2047; Available = 1998; Allocated = 49
16:22:22.8564159: Max = 2047; Available = 1997; Allocated = 50
16:22:22.8574181: Max = 2047; Available = 1997; Allocated = 50
16:22:22.8574181: Max = 2047; Available = 1997; Allocated = 50
16:22:22.8714252: Max = 2047; Available = 1997; Allocated = 50
16:22:22.8874562: Max = 2047; Available = 1997; Allocated = 50
16:22:23.8441074: Max = 2047; Available = 1996; Allocated = 51
16:22:23.8642096: Max = 2047; Available = 1996; Allocated = 51
16:22:23.8642096: Max = 2047; Available = 1996; Allocated = 51
16:22:23.8795669: Max = 2047; Available = 1996; Allocated = 51
16:22:23.8953462: Max = 2047; Available = 1996; Allocated = 51
16:22:24.8494102: Max = 2047; Available = 1995; Allocated = 52
16:22:24.8564175: Max = 2047; Available = 1995; Allocated = 52
16:22:24.8715401: Max = 2047; Available = 1995; Allocated = 52
16:22:24.8871801: Max = 2047; Available = 1995; Allocated = 52
16:22:24.8871801: Max = 2047; Available = 1995; Allocated = 52
16:22:25.8453296: Max = 2047; Available = 1995; Allocated = 52
16:22:25.8537084: Max = 2047; Available = 1994; Allocated = 53
16:22:25.8537084: Max = 2047; Available = 1994; Allocated = 53
16:22:25.8537084: Max = 2047; Available = 1994; Allocated = 53
16:22:25.8771622: Max = 2047; Available = 1994; Allocated = 53
16:22:26.8434406: Max = 2047; Available = 1993; Allocated = 54
16:22:26.8434406: Max = 2047; Available = 1993; Allocated = 54
16:22:26.8561548: Max = 2047; Available = 1993; Allocated = 54
16:22:26.8561548: Max = 2047; Available = 1993; Allocated = 54
16:22:26.8561548: Max = 2047; Available = 1993; Allocated = 54
16:22:27.8604018: Max = 2047; Available = 1992; Allocated = 55
16:22:27.8604018: Max = 2047; Available = 1992; Allocated = 55
16:22:27.8874279: Max = 2047; Available = 1992; Allocated = 55
16:22:27.8874279: Max = 2047; Available = 1992; Allocated = 55
16:22:27.8874279: Max = 2047; Available = 1992; Allocated = 55
16:22:28.8482569: Max = 2047; Available = 1992; Allocated = 55
16:22:28.8501816: Max = 2047; Available = 1991; Allocated = 56
16:22:28.8674081: Max = 2047; Available = 1991; Allocated = 56
16:22:28.8794092: Max = 2047; Available = 1991; Allocated = 56
16:22:28.8794092: Max = 2047; Available = 1991; Allocated = 56
16:22:29.8415873: Max = 2047; Available = 1990; Allocated = 57
16:22:29.8686246: Max = 2047; Available = 1990; Allocated = 57
16:22:29.8686246: Max = 2047; Available = 1990; Allocated = 57
16:22:29.8839546: Max = 2047; Available = 1990; Allocated = 57
16:22:29.9000910: Max = 2047; Available = 1990; Allocated = 57
16:22:30.8505474: Max = 2047; Available = 1989; Allocated = 58
16:22:30.8505474: Max = 2047; Available = 1989; Allocated = 58
16:22:30.8573737: Max = 2047; Available = 1989; Allocated = 58
16:22:30.8877856: Max = 2047; Available = 1989; Allocated = 58
16:22:30.8877856: Max = 2047; Available = 1989; Allocated = 58
16:22:30.9031657: Max = 2047; Available = 1989; Allocated = 58
16:22:30.9031657: Max = 2047; Available = 1989; Allocated = 58
16:22:30.9031657: Max = 2047; Available = 1989; Allocated = 58
16:22:30.9031657: Max = 2047; Available = 1989; Allocated = 58
16:22:30.9031657: Max = 2047; Available = 1989; Allocated = 58
16:22:30.9031657: Max = 2047; Available = 1989; Allocated = 58
16:22:30.9031657: Max = 2047; Available = 1989; Allocated = 58
16:22:30.9186140: Max = 2047; Available = 1989; Allocated = 58
16:22:31.8584929: Max = 2047; Available = 1988; Allocated = 59
16:22:31.8584929: Max = 2047; Available = 1988; Allocated = 59
16:22:31.8594843: Max = 2047; Available = 1988; Allocated = 59
16:22:31.8834716: Max = 2047; Available = 1988; Allocated = 59
16:22:31.8834716: Max = 2047; Available = 1988; Allocated = 59
16:22:31.8834716: Max = 2047; Available = 1988; Allocated = 59
16:22:32.8621705: Max = 2047; Available = 1987; Allocated = 60
16:22:32.8621705: Max = 2047; Available = 1987; Allocated = 60
16:22:32.8621705: Max = 2047; Available = 1987; Allocated = 60
16:22:32.8621705: Max = 2047; Available = 1987; Allocated = 60
16:22:32.8726795: Max = 2047; Available = 1987; Allocated = 60
16:22:32.8884519: Max = 2047; Available = 1987; Allocated = 60
16:22:33.8463528: Max = 2047; Available = 1987; Allocated = 60
16:22:33.8562661: Max = 2047; Available = 1986; Allocated = 61
16:22:33.8774919: Max = 2047; Available = 1986; Allocated = 61
16:22:33.8774919: Max = 2047; Available = 1986; Allocated = 61
16:22:33.8932274: Max = 2047; Available = 1986; Allocated = 61
16:22:33.9082579: Max = 2047; Available = 1986; Allocated = 61
16:22:34.8595663: Max = 2047; Available = 1985; Allocated = 62
16:22:34.8595663: Max = 2047; Available = 1985; Allocated = 62
16:22:34.8595663: Max = 2047; Available = 1985; Allocated = 62
16:22:34.8855730: Max = 2047; Available = 1985; Allocated = 62
16:22:34.9006122: Max = 2047; Available = 1985; Allocated = 62
16:22:34.9006122: Max = 2047; Available = 1985; Allocated = 62
16:22:35.8482215: Max = 2047; Available = 1985; Allocated = 62
16:22:35.8639664: Max = 2047; Available = 1984; Allocated = 63
16:22:35.8689794: Max = 2047; Available = 1984; Allocated = 63
16:22:35.8689794: Max = 2047; Available = 1984; Allocated = 63
16:22:35.8689794: Max = 2047; Available = 1984; Allocated = 63
16:22:35.8788829: Max = 2047; Available = 1984; Allocated = 63
16:22:36.8567572: Max = 2047; Available = 1983; Allocated = 64
16:22:36.8617741: Max = 2047; Available = 1983; Allocated = 64
16:22:36.8617741: Max = 2047; Available = 1983; Allocated = 64
16:22:36.8627645: Max = 2047; Available = 1983; Allocated = 64
16:22:36.8627645: Max = 2047; Available = 1983; Allocated = 64
16:22:36.8627645: Max = 2047; Available = 1983; Allocated = 64
16:22:37.8486813: Max = 2047; Available = 1982; Allocated = 65
16:22:37.8668171: Max = 2047; Available = 1982; Allocated = 65
16:22:37.8668171: Max = 2047; Available = 1982; Allocated = 65
16:22:37.8988297: Max = 2047; Available = 1982; Allocated = 65
16:22:37.8988297: Max = 2047; Available = 1982; Allocated = 65
16:22:37.8988297: Max = 2047; Available = 1982; Allocated = 65
16:22:38.8668907: Max = 2047; Available = 1981; Allocated = 66
16:22:38.8778259: Max = 2047; Available = 1983; Allocated = 64
16:22:38.8937751: Max = 2047; Available = 1983; Allocated = 64
16:22:38.8937751: Max = 2047; Available = 1983; Allocated = 64
16:22:39.8430616: Max = 2047; Available = 1983; Allocated = 64
16:22:39.8740731: Max = 2047; Available = 1983; Allocated = 64
16:22:39.8740731: Max = 2047; Available = 1983; Allocated = 64
16:22:39.9050685: Max = 2047; Available = 1984; Allocated = 63
16:22:40.8621037: Max = 2047; Available = 1984; Allocated = 63
16:22:40.8621037: Max = 2047; Available = 1984; Allocated = 63
16:22:40.8931726: Max = 2047; Available = 1985; Allocated = 62
16:22:40.8931726: Max = 2047; Available = 1985; Allocated = 62
16:22:41.8711876: Max = 2047; Available = 1993; Allocated = 54
16:22:41.8711876: Max = 2047; Available = 1993; Allocated = 54
16:22:41.8871904: Max = 2047; Available = 1994; Allocated = 53
16:22:41.8871904: Max = 2047; Available = 1994; Allocated = 53
16:22:42.8755639: Max = 2047; Available = 1995; Allocated = 52
16:22:42.8755639: Max = 2047; Available = 1995; Allocated = 52
16:22:42.8755639: Max = 2047; Available = 1995; Allocated = 52
16:22:42.8909060: Max = 2047; Available = 1997; Allocated = 50
16:22:43.8489596: Max = 2047; Available = 1997; Allocated = 50
16:22:43.8649778: Max = 2047; Available = 1997; Allocated = 50
16:22:43.8809428: Max = 2047; Available = 1997; Allocated = 50
16:22:43.8963614: Max = 2047; Available = 1998; Allocated = 49
16:22:44.8701041: Max = 2047; Available = 1999; Allocated = 48
16:22:44.8701041: Max = 2047; Available = 1999; Allocated = 48
16:22:44.9011234: Max = 2047; Available = 2000; Allocated = 47
16:22:44.9171814: Max = 2047; Available = 2000; Allocated = 47
16:22:45.8777250: Max = 2047; Available = 2002; Allocated = 45
16:22:45.8777250: Max = 2047; Available = 2002; Allocated = 45
16:22:45.8777250: Max = 2047; Available = 2002; Allocated = 45
16:22:45.8931005: Max = 2047; Available = 2003; Allocated = 44
16:22:46.8674070: Max = 2047; Available = 2003; Allocated = 44
16:22:46.8674070: Max = 2047; Available = 2003; Allocated = 44
16:22:46.8674070: Max = 2047; Available = 2003; Allocated = 44
16:22:46.8674070: Max = 2047; Available = 2003; Allocated = 44
16:22:47.8612536: Max = 2047; Available = 2005; Allocated = 42
16:22:47.8771915: Max = 2047; Available = 2005; Allocated = 42
16:22:47.9072854: Max = 2047; Available = 2006; Allocated = 41
16:22:47.9072854: Max = 2047; Available = 2006; Allocated = 41
16:22:48.8932519: Max = 2047; Available = 2008; Allocated = 39
16:22:48.9083285: Max = 2047; Available = 2007; Allocated = 40
16:22:49.8486481: Max = 2047; Available = 2009; Allocated = 38
16:22:49.8787683: Max = 2047; Available = 2009; Allocated = 38
16:22:50.8689410: Max = 2047; Available = 2011; Allocated = 36
16:22:50.9000670: Max = 2047; Available = 2012; Allocated = 35
16:22:51.8732895: Max = 2047; Available = 2013; Allocated = 34
16:22:51.8883018: Max = 2047; Available = 2014; Allocated = 33
16:22:52.8783420: Max = 2047; Available = 2016; Allocated = 31
16:22:52.8944030: Max = 2047; Available = 2017; Allocated = 30
16:22:53.8717262: Max = 2047; Available = 2018; Allocated = 29
16:22:53.8872043: Max = 2047; Available = 2018; Allocated = 29
16:22:54.8757865: Max = 2047; Available = 2019; Allocated = 28
16:22:54.9078676: Max = 2047; Available = 2020; Allocated = 27
16:22:55.8850093: Max = 2047; Available = 2021; Allocated = 26
16:22:55.8850093: Max = 2047; Available = 2021; Allocated = 26
16:22:56.8792937: Max = 2047; Available = 2023; Allocated = 24
16:22:56.8792937: Max = 2047; Available = 2023; Allocated = 24
16:22:57.8695620: Max = 2047; Available = 2025; Allocated = 22
16:22:57.9154596: Max = 2047; Available = 2026; Allocated = 21
The thread 0x424c has exited with code 0 (0x0).
The thread 0x71f4 has exited with code 0 (0x0).
The thread 0x7320 has exited with code 0 (0x0).
The thread 0x4c40 has exited with code 0 (0x0).
The thread 0x2c54 has exited with code 0 (0x0).
The thread 0x1004 has exited with code 0 (0x0).
The thread 0x4008 has exited with code 0 (0x0).
The thread 0x4bdc has exited with code 0 (0x0).
The thread 0x864 has exited with code 0 (0x0).
The thread 0x2984 has exited with code 0 (0x0).
The thread 0x6444 has exited with code 0 (0x0).
The thread 0x6bc0 has exited with code 0 (0x0).
The thread 0x7ce4 has exited with code 0 (0x0).
The thread 0x8008 has exited with code 0 (0x0).
The thread 0x39a8 has exited with code 0 (0x0).
The thread 0x6284 has exited with code 0 (0x0).
The thread 0x31b8 has exited with code 0 (0x0).
The thread 0x4d9c has exited with code 0 (0x0).
The thread 0x69fc has exited with code 0 (0x0).
The thread 0x78d8 has exited with code 0 (0x0).
The thread 0x7a80 has exited with code 0 (0x0).
The thread 0x7f74 has exited with code 0 (0x0).
The thread 0x3a48 has exited with code 0 (0x0).
The thread 0x5288 has exited with code 0 (0x0).
The thread 0x284c has exited with code 0 (0x0).
The thread 0x7e34 has exited with code 0 (0x0).
The thread 0x38e8 has exited with code 0 (0x0).
The thread 0x7584 has exited with code 0 (0x0).
The thread 0x593c has exited with code 0 (0x0).
The thread 0x3638 has exited with code 0 (0x0).
The thread 0x2744 has exited with code 0 (0x0).
The thread 0x5504 has exited with code 0 (0x0).
The thread 0x47e8 has exited with code 0 (0x0).
The thread 0x6748 has exited with code 0 (0x0).
The thread 0x61ac has exited with code 0 (0x0).
The thread 0x4fc4 has exited with code 0 (0x0).
The thread 0x3988 has exited with code 0 (0x0).
The thread 0x6820 has exited with code 0 (0x0).
The thread 0x52f0 has exited with code 0 (0x0).
The thread 0x2fd8 has exited with code 0 (0x0).
The thread 0x3980 has exited with code 0 (0x0).
The thread 0x1670 has exited with code 0 (0x0).
The thread 0x5194 has exited with code 0 (0x0).
The thread 0x4594 has exited with code 0 (0x0).
The thread 0x7ee4 has exited with code 0 (0x0).
The thread 0x1544 has exited with code 0 (0x0).
The thread 0x839c has exited with code 0 (0x0).
The thread 0x3744 has exited with code 0 (0x0).
The thread 0x39e4 has exited with code 0 (0x0).
The thread 0x7e18 has exited with code 0 (0x0).
The thread 0x980 has exited with code 0 (0x0).
The thread 0x66dc has exited with code 0 (0x0).
The thread 0x2fd0 has exited with code 0 (0x0).
The thread 0x51a4 has exited with code 0 (0x0).
The thread 0x7aa8 has exited with code 0 (0x0).
The thread 0x277c has exited with code 0 (0x0).
The thread 0x82b0 has exited with code 0 (0x0).
The thread 0x35b8 has exited with code 0 (0x0).
The thread 0x6374 has exited with code 0 (0x0).
The thread 0x2d74 has exited with code 0 (0x0).
The thread 0x538 has exited with code 0 (0x0).
The thread 0x74d8 has exited with code 0 (0x0).
The thread 0x4364 has exited with code 0 (0x0).
The thread 0x7980 has exited with code 0 (0x0).
The thread 0x7d00 has exited with code 0 (0x0).
The thread 0x6010 has exited with code 0 (0x0).
As you can see, there are only 8 threads to begin with and the number slowly increases until there are no more queued tasks, at which point the number slowly decreases. The number of available threads is not actually the number of threads sitting around waiting to be used, but the number of threads that can still be created until you are at the maximum.
 
That would exasperate the issue.
You might become exasperated if the issue were exacerbated and you couldn't find the cause. ;)
 
.NET Core has added a few properties to the ThreadPool class and so I was able to run this code in a .NET 5 project and get an even clearer picture of what's going on:
C#:
private void button1_Click(object sender, EventArgs e)
{
    for (int i = 0; i < 100; i++)
    {
        ThreadPool.QueueUserWorkItem(DoWork);
    }
}

private void DoWork(object dummy)
{
    for (int i = 0; i < 3; i++)
    {
        ThreadPool.GetMaxThreads(out var maxWorkerThreads, out _);
        ThreadPool.GetAvailableThreads(out var availableWorkerThreads, out _);
        Debug.WriteLine($"{DateTime.Now.TimeOfDay}: Available = {availableWorkerThreads}; Created = {ThreadPool.ThreadCount}; In Use = {maxWorkerThreads - availableWorkerThreads}; Pending = {ThreadPool.PendingWorkItemCount}; Completed = {ThreadPool.CompletedWorkItemCount}");
        Thread.Sleep(10000);
    }
}
I won't post the long output again because you can run it for yourself but points I noted in my test included:
  • The default maximum number of threads was 2^15 rather than 2^11.
  • The ThreadCount property rose steadily as long as the PendingWorkItem property was greater than zero, at which point it remained the same thereafter.
  • The initial value of PendingWorkItemCount was 92, indicating that the initial 8 threads immediately had a work item allocated.
  • The initial value of CompletedWorkItemCount was in the teens, even moments after the application was started. In one test it was 19 and in another it was 16.
  • The final value of CompletedWorkItemCount was in the 120s, showing that the ThreadPool was at work for more than just our tasks.
 
And here's the magic text we were looking for, but unfortunately in a different part of the documentation... the introduction to managed thread pool, rather than the ThreadPool class documentation:

Maximum number of thread pool threads​

The number of operations that can be queued to the thread pool is limited only by available memory. However, the thread pool limits the number of threads that can be active in the process simultaneously. If all thread pool threads are busy, additional work items are queued until threads to execute them become available.

 
Back
Top Bottom