Parallel.ForEach not always working

Nathan Sokalski 4,111 Reputation points
2021-09-29T21:35:15.34+00:00

I have the following do loop:

do
{
changed = false;

//foreach (SquareSet ss in this.HorizontalSets) { changed = changed | ss.Solve(); }
//foreach (SquareSet ss in this.VerticalSets) { changed = changed | ss.Solve(); }

//Task.Run(() => { Parallel.ForEach(this.HorizontalSets, (ss) => { changed = changed | ss.Solve(); }); }).Wait();
//Task.Run(() => { Parallel.ForEach(this.VerticalSets, (ss) => { changed = changed | ss.Solve(); }); }).Wait();

//changed = changed | this.HorizontalSets.Select((ss) => { return ss.Solve(); }).Any((setchanged) => { return setchanged; });
//changed = changed | this.VerticalSets.Select((ss) => { return ss.Solve(); }).Any((setchanged) => { return setchanged; });

//changed = changed | this.HorizontalSets.AsParallel().Select((ss) => { return ss.Solve(); }).Any((setchanged) => { return setchanged; });
//changed = changed | this.VerticalSets.AsParallel().Select((ss) => { return ss.Solve(); }).Any((setchanged) => { return setchanged; });
} while (changed);

Each pair of statements executes the Solve() method on all the items in a List, and keeps a cumulative Boolean of the results. I want to do this using all CPUs, so I tried the 2nd & 4th techniques (using Parallel.ForEach & PLinq). The 1st, 3rd, & 4th techniques seem to always work, but the 2nd (the one using Parallel.ForEach) only seems to work sometimes. The first set of values (the values in this.HorizontalSets) must be finished before doing the second set of values (the values in this.VerticalSets). I think the code is somehow starting the second set before finishing all items in the first, but I am not exactly sure how to make sure it does not do this. Is there something I am doing wrong?

Developer technologies C#
0 comments No comments
{count} votes

Accepted answer
  1. Viorel 122.5K Reputation points
    2021-09-30T01:35:30.637+00:00

    It seems that you did not try this yet:

    int c = 0;
    Parallel.ForEach( this.HorizontalSets, ss => { if( ss.Solve( ) ) Interlocked.Increment( ref c ); } );
    Parallel.ForEach( this.VerticalSets, ss => { if( ss.Solve( ) ) Interlocked.Increment( ref c ); } );
    changed = c != 0;
    

    (In new .NET you can use Interlocked.Or).

    It is not clear if you can also use a single ForEach:

    Parallel.ForEach( this.HorizontalSets.Concat( this.VerticalSets ), ss => { if( ss.Solve( ) ) Interlocked.Increment( ref c ); } );
    

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.