Track progress when using TPL's Parallel.ForEach
Solution 1
Jon's solution is good, if you need simple synchronization like this, your first attempt should almost always use lock
. But if you measure that the locking slows things too much, you should think about using something like Interlocked
.
In this case, I would use Interlocked.Increment
to increment the current count, and change Progress
into a property:
private long total;
private long current;
public double Progress
{
get
{
if (total == 0)
return 0;
return (double)current / total;
}
}
…
this.total = Products.LongCount();
this.current = 0;
Parallel.ForEach(Products, product =>
{
try
{
var price = GetPrice(SystemAccount, product);
SavePrice(product, price);
}
finally
{
Interlocked.Increment(ref this.current);
}
});
Also, you might want to consider what to do with exceptions, I'm not sure that iterations that ended with an exception should be counted as done.
Solution 2
Since you are just doing a few quick calculations, ensure atomicity by locking on an appropriate object:
long total = Products.LongCount();
long current = 0;
double Progress = 0.0;
var lockTarget = new object();
Parallel.ForEach(Products, product =>
{
try
{
var price = GetPrice(SystemAccount, product);
SavePrice(product,price);
}
finally
{
lock (lockTarget) {
Progress = ++this.current / total;
}
}});
Solution 3
A solution without using any blocking in the body:
long total = Products.LongCount();
BlockingCollection<MyState> states = new BlockingCollection<MyState>();
Parallel.ForEach(Products, () =>
{
MyState myState = new MyState();
states.Add(myState);
return myState;
},
(i, state, arg3, myState) =>
{
try
{
var price = GetPrice(SystemAccount, product);
SavePrice(product,price);
}
finally
{
myState.value++;
return myState;
}
},
i => { }
);
Then, to access the current progress:
(float)states.Sum(state => state.value) / total
Madu Alikor
I'm a software enthusiast, architect and philosopher and work for a company called thoughtworks. I love F#, C# and Golang
Updated on June 05, 2022Comments
-
Madu Alikor over 1 year
What is the best way way to track progress in the following
long total = Products.LongCount(); long current = 0; double Progress = 0.0; Parallel.ForEach(Products, product => { try { var price = GetPrice(SystemAccount, product); SavePrice(product,price); } finally { Interlocked.Decrement(ref this.current); }});
I want to update the progress variable from 0.0 to 1.0 (current/total) but i don't want to use anything that would have an adverse effect on the parallelism.