I'm trying to implement the Parallel.ForEach
pattern and track progress, but I'm missing something regarding locking. The following example counts to 1000 when the threadCount = 1
, but not when the threadCount
> 1. What is the correct way to do this?
class Program
{
static void Main()
{
var progress = new Progress();
var ids = Enumerable.Range(1, 10000);
var threadCount = 2;
Parallel.ForEach(ids, new ParallelOptions { MaxDegreeOfParallelism = threadCount }, id => { progress.CurrentCount++; });
Console.WriteLine("Threads: {0}, Count: {1}", threadCount, progress.CurrentCount);
Console.ReadKey();
}
}
internal class Progress
{
private Object _lock = new Object();
private int _currentCount;
public int CurrentCount
{
get
{
lock (_lock)
{
return _currentCount;
}
}
set
{
lock (_lock)
{
_currentCount = value;
}
}
}
}
ForEach functionally equivalent to continue in a foreach loop? Parallel. ForEach(items, parallelOptions, item => { if (! isTrue) continue; });
We can restrict the number of concurrent threads created during the execution of a parallel loop by using the MaxDegreeOfParallelism property of ParallelOptions class.
Parallel. ForEach uses managed thread pool to schedule parallel actions. The number of threads is set by ThreadPool.
The execution of Parallel. Foreach is faster than normal ForEach.
The usual problem with calling something like count++
from multiple threads (which share the count
variable) is that this sequence of events can happen:
count
.count
.count
.count
.This way, the value written by thread A is overwritten by thread B, so the value is actually incremented only once.
Your code adds locks around operations 1, 2 (get
) and 5, 6 (set
), but that does nothing to prevent the problematic sequence of events.
What you need to do is to lock the whole operation, so that while thread A is incrementing the value, thread B can't access it at all:
lock (progressLock)
{
progress.CurrentCount++;
}
If you know that you will only need incrementing, you could create a method on Progress
that encapsulates this.
Old question, but I think there is a better answer.
You can report progress using Interlocked.Increment(ref progress)
that way you do not have to worry about locking the write operation to progress.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With