Parallel.ForEach doesn't make use of all available thread pool threads

13,023

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 Tasks 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

Share:
13,023
user183872
Author by

user183872

Updated on June 05, 2022

Comments

  • user183872
    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
    user183872 over 8 years
    Ok 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
    Andrey Nasonov over 8 years
    Set both MaxDegreeOfParallelism to 100 and MinThreads to 100.
  • Andrey Nasonov
    Andrey Nasonov over 8 years
    If you are using async/await, consider using Task.Delay instead of Thread.Sleep.
  • user183872
    user183872 over 8 years
    Should 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?