views:

124

answers:

2

I am running a multi-threaded loop:

protected ParallelOptions parallelOptions = new ParallelOptions();

parallelOptions.MaxDegreeOfParallelism = 2;
Parallel.ForEach(items, parallelOptions, item =>
{
// Loop code here
});

I want to change the parallelOptions.MaxDegreeOfParallelism during the execution of the parallel loop to reduce or increase the amount of threads.

parallelOptions.MaxDegreeOfParallelism = 5;

It dosen't see to increase the threads. Does anyone have any ideas?

+1  A: 

I wouldn't expect it to be possible to change the degree of parallelism after you've called ForEach. As I understand it, ForEach is going to determine how many threads it can create, create that many partitions, and create the threads to operate on those partitions. There's no point at which it can say, "Oh, wait, he changed our resource allocation, let me re-partition the array and re-allocate threads."

Jim Mischel
+1  A: 

The issue with even trying to do this is that it's a hard problem. For starters, how do you even observe CPU and disk utilization reliably? Sampling CPU infrequently will give a poor picture of what's actually going on and sampling disk utilization is even harder. Secondly, what is the granularity of your tasks and how often can you quickly can you actually change the number that are running. Thirdly things change rapidly over time so you need to apply some kind of filtering to your observations. Fourthly, the ideal number of threads will depend on the CPU that the code is actually running on. Fifthly, if you allocate too many threads you'll be thrashing between them instead of doing useful work.

See http://msdn.microsoft.com/en-us/magazine/ff960958.aspx for a discussion on how the Thread Pool in .NET handles the complex task of deciding how many threads to use.

You could also use reflector and take a look at the code that TPL uses to allocate threads and to avoid unnecessary context switching - it's complex and that's not even taking disk access into account!

You could instead try executing the tasks on a lower priority thread (creating your own TaskScheduler that runs threads with a priority of below-normal is actually quite easy). That at least will ensure that you can run up 100% CPU without impacting the rest of the system. Messing with thread priorities is in itself fraught with problems but if this is a purely background task it can be straightforward and might help.

Often though, disk utilization is the real culprit when it comes to other applications suffering at the hands of one greedy application. Windows can allocate CPU fairly between applications with ease but when relatively slow disk access is involved it's quite another matter. Instead of trying to dynamically adjust how many thread you have running you may instead need to simply throttle your application such that it doesn't access the disk too often. That's something you can do without changing how many threads are active.

You could also look at SetPriorityClass as a way to inform the OS that your process is less important than other applications running on the system, see http://stackoverflow.com/questions/301290/how-can-i-o-priority-of-a-process-be-increased for more information. But that assumes your whole process is less important, not just this part of it.

Hightechrider