Anthony
Anthony

Reputation: 12397

Wait Handles and the System.Threading.Tasks namespace

When I use Parallel.ForEach or Parallel.For in the System.Threading.Tasks namespace, can I assume that all threads are synchronised before execution continues?

Is there something equivalent to

WaitHandle.WaitAll(...);

going on?

So If I call something like

Parallel.ForEach(collection, DoSomethingWithObject);

Can I be certain that each call to DoSomethingWithObject has completed before ForEach returns, or do I need to use WaitHandles myself?

Upvotes: 2

Views: 847

Answers (4)

user370770
user370770

Reputation:

Parallel.ForEach() and Parallel.For() will block until all of its tasks have ended, either because they completed successfully or because they were canceled. So yes, all threads are synchronized before execution continues.

Upvotes: 0

user315772
user315772

Reputation:

All the tasks will have completed by the time Parallel.ForEach() returns, unless a call to Stop() was specifically made to abort the parallel execution. In such cases, the .IsCompleted property of the ParallelLoopState object returned by Parallel.ForEach() will be false.

You can read about it here:

http://msdn.microsoft.com/en-us/library/dd992001.aspx

http://msdn.microsoft.com/en-us/library/system.threading.tasks.parallelloopresult.aspx

You can also write a small test application where DoSomethingWithObject() is a very long running operation, or a call to Thread.Sleep(), to verify this. Execution will block until all threads have finished sleeping.

Sample:

Parallel.ForEach(Enumerable.Range(1, 8),
    i =>
    {
        Thread.Sleep(5000);
        Console.WriteLine(i + " done!");
    });
Console.WriteLine("All done!");

Upvotes: 2

Brian Gideon
Brian Gideon

Reputation: 48949

Can I be certain that each call to DoSomethingWithObject has completed before ForEach returns, or do I need to use WaitHandles myself?

Parallel.ForEach is a blocking call. It will not return until every iteration has completed.

Is there something equivalent to WaitHandle.WaitAll(...); going on?

Yes, more or less. It would not actually use WaitHandle.WaitAll because 1) it would be horribly inefficient and 2) it is not scalable since it has a 64 handle limit. There are a few very scalable techniques for waiting on multiple tasks to complete. One of my favorites is to use CountdownEvent.1

Here is how it works. Use your imagination and pretend Parallel.ForEach gets expanded into the following code.2

var finished = new CountdownEvent(1);
foreach (var item in collection)
{
  var capture = item;
  finished.AddCount();
  Task.Factory.StartNew(
    () =>
    {
      try
      {
        DoSomething(capture);
      }
      finally
      {
        finished.Signal();
      }
    });
}
finished.Signal();
finished.Wait();

1I am not saying this is the technique used by Parallel.ForEach, but it is probably something similar.

2In reality Parallel.ForEach is implemented in a much more complex manner. I am only trying to help you visualize how it might wait for each iteration to complete.

Upvotes: 2

Polity
Polity

Reputation: 15130

To extend on Phong's correct answer, Parallel.For and Parallel.ForEach internally create a so called ParallelXXXReplicationTask which is run synchronously and spawns X number of child tasks where X is the effective amount of concurrency level (default Environment.ProcessorCount). Each child task synchronizes with a Semphore which releases the Parent task once set (count to 0) which ensures that the execution of the parent task will only return once all child tasks have finished (or crashed/stopped which is determined through synchronized settings). Since the parentTask is ran synchronously and is being waited for. You can ensure that whenever your call to a Parallel method returns, all iterations have been finished effectively.

Upvotes: 3

Related Questions