In the past I have done this:
List<Item> _Items = GetItems();
int _CountDown = _Items.Count;
using (BackgroundWorker _Worker = new BackgroundWorker())
{
_Worker.DoWork += (s, arg) =>
{
DoSomething(_Items[_CountDown]);
};
_Worker.RunWorkerCompleted += (s, arg) =>
{
if (System.Threading.Interlocked.Decrement(ref _CountDown) == 0)
RaiseAllDoneEvent();
else
_Worker.RunWorkerAsync();
};
_Worker.RunWorkerAsync();
}
With Parallel I do somethign like this:
List<Item> _Items = GetItems();
int _CountDown = _Items.Count;
System.Threading.Tasks.Parallel.ForEach(_Items, (i) =>
{
DoSomething(i);
if (System.Threading.Interlocked.Decrement(ref _CountDown) == 0)
RaiseAllDoneEvent();
});
My real question is: is Interlocked.Decrement() correct here?
Parallel.ForEach will block until all of the items are processed- the right thing to do here seems to be to call RaiseAllDoneEvent() from the caller, immediately after calling Parallel.ForEach:
In other words, no need to count down (or keep track of how many items there are to process) at all. If you want the parallel operations to not block, you can turn it into a bunch of tasks:
In that case, you’d be starting an outer task to spin up and then wait on a bunch of tasks (one for each item), and then finally raise the all done event. None of this will block the original caller.