views:

85

answers:

4

I am trying to execute parallel fuctions on an list of objects using the new 4.0 Parallel.ForEach function. This is a very long maintenance process. I would like to make it execute in the order of the list so that I can stop and continue execution at the previous point. How do I do this?

Here is an example. I have a list of objects, a1 to a100. This is the current order: a1, a51, a2, a52, a3, a53...

I want this order: a1, a2, a3, a4...

I am ok with some objects being run out of order, but as long as I can find a point in the list where I can say that all objects before this point were run. I read the parallel programming csharp whitepaper and didnt see anything about it. There isnt a setting for this in the ParallelOptions class.

+2  A: 

Read this:

PLINQ’s Ordering Model

When To Use Parallel.ForEach and When to Use PLINQ

Leniel Macaferi
Thanks, I'll read these and see if PLINQ works. I'm not running any queries though... I'll give it a try.
Jeff Z
@Jeff Z - have you solved your problem? If yes, what have you used?
Leniel Macaferi
@Leniel - Sorry for the delayed response. I was able to do this with PLINQ (AsParallel and AsOrdered). I had another project pushed onto me before I could finish this one. I'm still not done but this pointed me in the right direction. Thanks again.
Jeff Z
A: 

I don't know if this agrees with @Leniel's links, but this is what popped into my mind (untested).

List<SomeObject> objects = GetData(); // or whatever

Parallel.ForEach( 
    objects.OrderBy( so => so.Id ), // whatever you want to order by
    current =>{ 
       // do stuff.
    } 
);
BioBuckyBall
The list is already ordered, but Parallel.ForEach partitions the list based on the thread count and runs those partitions in order. Thanks for responding though.
Jeff Z
@user400775 ah, yes the partitioning is changing the order. I guess you'll have to read through @Leniel's links :)
BioBuckyBall
+1  A: 

As an alternate suggestion, you could record which object have been run and then filter the list when you resume exection to exclude the objects which have already run.

If this needs to be persistent across application restarts, you can store the ID's of the already executed objects (I assume here the objects have some unique identifier).

Chris Taylor
Thanks Chris. I tested with the PLINQ and that is sufficient but I will also keep a list of excluded objects.
Jeff Z
+1  A: 

If you use Parallel.Break to terminate the loop then you are guarenteed that all indices below the returned value will have been executed. This is about as close as you can get. The example here uses For but ForEach has similar overloads.

int n = ...
var result = new double[n];

var loopResult = Parallel.For(0, n, (i, loopState) =>
{
   if (/* break condition is true */)
   {
      loopState.Break();
      return;
   }
   result[i] = DoWork(i);
});

if (!loopResult.IsCompleted && 
        loopResult.LowestBreakIteration.HasValue)
{
   Console.WriteLine("Loop encountered a break at {0}", 
                      loopResult.LowestBreakIteration.Value);
}

In a ForEach loop, an iteration index is generated internally for each element in each partition. Execution takes place out of order but after break you know that all the iterations lower than LowestBreakIteration will have been completed.

Taken from "Parallel Programming with Microsoft .NET" http://parallelpatterns.codeplex.com/

Available on MSDN. See http://msdn.microsoft.com/en-us/library/ff963552.aspx. The section "Breaking out of loops early" covers this scenario.

See also: http://msdn.microsoft.com/en-us/library/dd460721.aspx

Ade Miller