tags:

views:

108

answers:

2

i have an extension method for IEnumerable which then iterates through the collection, doing its business, and then returns an new IEnumerable.

I have tried to use PLINQ using .AsParallel().ForAll() which speeds up the iterations significantly (which of course it should do) however when the collection is returned, there are often a few objects in that collection that are null.

I'm assuming this may be because it is returning the collection before all the 'business' has a chance to complete? if i debug and put in a breakpoint, there are no nulls.

is there some sort of 'wait for this operation to be completed' method that i should be using?

EDIT: to be a bit clearer, there is business logic in the forall, modifying properties etc. it's necessary to have an action looped, rather than simply selecting something.

+1  A: 

The answer depends on what you mean by returning, because the ForAll method doesn't return anything. It invokes the delegate you specify in parallel for all elements of the collection. I would suppose that your code looks like this:

data.AsParallel().ForAll(() => /* calculate and store result somewhere */);
// more code

The ForAll method doesn't wait for all delegates to complete, so more code can execute before all the delegates complete (and you also need to be careful in the store result somewhere bit, because it may run concurrently for multiple delegates!)

I think the code could be rewritten more elegantly using the Select method:

var res = data.AsParallel().Select(() => /* calculate the result */);

In this case, the delegate simply returns the result. The Where method collects all the results and when you iterate over the returned IEnumerable, it guarantees that all delegates finished calculating.

Tomas Petricek
thanks tom - sorry for the ambiguity, the method returns the collection, it can't be returned from the forall - there's too much business logic in there.
benpage
In that case, you need to be really careful, because actions are executing concurrently - you almost certainly need some locks. PLINQ is best used when you have code that "returns a result" (aka is functional) rather than "modifies a state" (aka is imperative).
Tomas Petricek
A: 

ForAll() doesn't perform a merge, and returns immediately. Parallel.ForEach() is probably the functionality you're looking for.

ie instead of:

collection.AsParallel().ForAll( t=>t.doWork() );

something like

Parallel.ForEach(collection.AsParallel(), t=>t.doWork());

Tanzelax
hmmm.. no good - Parallel.ForEach(collection, t=>t.doWOrk()); causes same problem, and Parallel.ForEach(collection.AsParallel(), t=>t.doWork()); throws an aggregate exception
benpage
Odd... `Parallel.ForEach()` returns a `ParallelLoopResult` which has an `IsCompleted` property that you could check. I'd look into the contents of the `AggregateException` though, maybe something isn't happening correctly in your `doWork()` function.
Tanzelax