Parallel.ForEach doesn't make use of all available thread pool threads
Solution 1
Parallel.ForEach
uses managed thread pool to schedule parallel actions. The number of threads is set by ThreadPool.SetMinThreads
and ThreadPool.SetMaxThreads
. By default, the minimum number of threads is set to the number of processors on a system.
To minimize the usage of system resources, the number of pool threads is kept as low as possible. When all the pool threads are busy executing actions, the scheduler gradually spawns new threads.
The value MaxDegreeOfParallelism
is usually used to prevent Parallel.For
from scheduling more than the specified number of tasks simultaneously. It is useful in case of long computations when there is no sense of using more threads than the number of cores.
If you modify the code by increasing the sleep time Thread.Sleep(100000);
, you will see the creation of new threads.
If you call ThreadPool.SetMinThreads(100, 100);
before Parallel.ForEach
, you will see all 100
actions started simultaneously.
Solution 2
You will get the best performance if the number of threads doesn't exceed the number of processing cores.
Each core can only process one thread at a time. If there are more threads than cores, the OS has to switch between threads. Context switching is an expensive operation, you should try to avoid it in multi-threaded applications.
If the operations you perform are IO-bound, you should use Task
s instead of Paraller.For
. It's nicely explained on Scott Hanselman's blog.
The details of Parallel.For
thread management are explained in details in Andrey Nasonov's answer, so I will not repeat it.
If you want to learn more about threading, TPL and asynchrounous I/O I recommend CLR via C# book
user183872
Updated on June 05, 2022Comments
-
user183872 almost 2 years
Why when I run the following example do I only have the Parallel.ForEach run the number of threads equal to the number of cores on my machine? I thought Parallel.ForEach gives you thread pool threads of which there are approx 1000?
int threads1; int threads2; ThreadPool.GetAvailableThreads(out threads1,out threads2); var list = Enumerable.Range(1, 200); var po = new ParallelOptions { MaxDegreeOfParallelism = 100 }; Parallel.ForEach(list, po, x => { Console.WriteLine("Thread:" + Thread.CurrentThread.ManagedThreadId); Thread.Sleep(1000); });
Am I missing something here?
-
user183872 over 8 yearsOk I used the default MaxDegreeOfParallelism and then increased the timeout and sure enough it gradually increases the number of threads. My real-world application (unlike the example) uses async/await tasks with database I/O. I'm hoping to get more than 8+ threads (based on cores) to work in parallel as most of it is I/O bound to the database. How can I guarantee it will make most of the 1000+ threads available?
-
Andrey Nasonov over 8 yearsSet both MaxDegreeOfParallelism to 100 and MinThreads to 100.
-
Andrey Nasonov over 8 yearsIf you are using async/await, consider using
Task.Delay
instead ofThread.Sleep
. -
user183872 over 8 yearsShould I then use the ForEach<Async> instead of Parallel.ForEach if the tasks in each loop (4 in my case for each entry in the loop) are async?