25

And by "same thing" I mean do these two operations basically do the same work, and it just boils down to which one is more convenient to call based on what you have to work with? (i.e. a list of delegates or a list of things to iterate over)? I've been searching MSDN, StackOverflow, and various random articles but I have yet to find a clear answer for this.

EDIT: I should have been clearer; I am asking if the two methods do the same thing because if they do not, I would like to understand which would be more efficient.

Example: I have a list of 500 key values. Currently I use a foreach loop that iterates through the list (serially) and performs work for each item. If I want to take advantage of multiple cores, should I simply use Parallel.ForEach instead?
Let's say for arguments's sake that I had an array of 500 delegates for those 500 tasks - would the net effect be any different calling Parallel.Invoke and giving it a list of 500 delegates?

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
Brad Gagne
  • 393
  • 1
  • 3
  • 7

4 Answers4

35

Parallel.ForEach goes through the list of elements and can perform some task on the elements of the array.

eg.

Parallel.ForEach(val, (array) => Sum(array));

Parallel.Invoke can invoke many functions in parallel.

eg.

Parallel.Invoke(
() => doSum(array),
() => doAvg(array),
() => doMedian(array));

As from the example above, you can see that they are different in functionality. ForEach iterates through a List of elements and performs one task on each element in parallel, while Invoke can perform many tasks in parallel on a single element.

Emil
  • 1,131
  • 13
  • 23
Mayank
  • 8,777
  • 4
  • 35
  • 60
  • 2
    "Parallel.ForEach ... can perform some task on the elements of the array." So what if my task is to invoke a function? Any real difference vs Parallel.Invoke in terms of how the work gets done, and whether one will outperform the other? – Brad Gagne Jun 03 '12 at 02:47
  • 1. `Parallel.ForEach(val, (array) => Sum(array));` is calling function `Sum()`, so yes it can invoke a function. 2. I think it just depends on what you need to do. If you are invoking a single function on list of elements than `Parallel.ForEach` seems like a good choice, but if you are invoking more then one function on `List` of elements or single element than `Parallel.Invoke` will work best. – Mayank Jun 03 '12 at 02:57
  • What is meant by "element"? This part is not clear for me. What does it mean to perform many tasks on a single element? What is a single element? – Joseph Katzman Jul 13 '23 at 09:48
12

Parallel.Invoke and Parallel.ForEach (when used to execute Actions) function the same, although yes one specifically wants the collection to be an Array. Consider the following sample:

List<Action> actionsList = new List<Action>
            {
                () => Console.WriteLine("0"),
                () => Console.WriteLine("1"),
                () => Console.WriteLine("2"),
                () => Console.WriteLine("3"),
                () => Console.WriteLine("4"),
                () => Console.WriteLine("5"),
                () => Console.WriteLine("6"),
                () => Console.WriteLine("7"),
                () => Console.WriteLine("8"),
                () => Console.WriteLine("9"),
            };

            Parallel.ForEach<Action>(actionsList, ( o => o() ));

            Console.WriteLine();

            Action[] actionsArray = new Action[]
            {
                () => Console.WriteLine("0"),
                () => Console.WriteLine("1"),
                () => Console.WriteLine("2"),
                () => Console.WriteLine("3"),
                () => Console.WriteLine("4"),
                () => Console.WriteLine("5"),
                () => Console.WriteLine("6"),
                () => Console.WriteLine("7"),
                () => Console.WriteLine("8"),
                () => Console.WriteLine("9"),
            };

            Parallel.Invoke(actionsArray);

            Console.ReadKey();

This code produces this output on one Run. It's output is generally in a different order every time.

0 5 1 6 2 7 3 8 4 9

0 1 2 4 5 6 7 8 9 3

Jason Sturges
  • 15,855
  • 14
  • 59
  • 80
plukich
  • 635
  • 1
  • 6
  • 14
  • Thanks plukich. In my particular case I have some "fire-and-forget" work to be performed, so I am not concerned with return values or even what order the work is performed in. So I was purely curious with whether there was any difference/benefit to calling one or the other. – Brad Gagne Jun 03 '12 at 02:41
  • In my experiences, they behave the same. This may be more effort than you want, but if you download the latest .NET Reflector and open mscorlib.dll, you can see the exact answer to your question by looking into the Parallel class in System.Threading.Tasks. That's what I would do. – plukich Jun 03 '12 at 03:15
2

Surprisingly, no, they are not the same thing. Their fundamental difference is on how they behave in case of exceptions:

  1. The Parallel.ForEach (as well as the Parallel.For and the upcoming Parallel.ForEachAsync) fails fast. After an exception has occurred, it does not start any new parallel work, and will return as soon as all the currently running delegates complete.
  2. The Parallel.Invoke invokes invariably all the actions, regardless of whether some (or all) of them failed.

For demonstrating this behavior, lets run in parallel 1,000 actions, with one every three actions failing:

int c = 0;
Action[] actions = Enumerable.Range(1, 1000).Select(n => new Action(() =>
{
    Interlocked.Increment(ref c);
    if (n % 3 == 0) throw new ApplicationException();
})).ToArray();

try { c = 0; Parallel.For(0, actions.Length, i => actions[i]()); }
catch (AggregateException aex)
{ Console.WriteLine($"Parallel.For, Exceptions: {aex.InnerExceptions.Count}/{c}"); }

try { c = 0; Parallel.ForEach(actions, action => action()); }
catch (AggregateException aex)
{ Console.WriteLine($"Parallel.ForEach, Exceptions: {aex.InnerExceptions.Count}/{c}"); }

try { c = 0; Parallel.Invoke(actions); }
catch (AggregateException aex)
{ Console.WriteLine($"Parallel.Invoke, Exceptions: {aex.InnerExceptions.Count}/{c}"); }

Output (in my PC, .NET 5, Release build):

Parallel.For, Exceptions: 5/12
Parallel.ForEach, Exceptions: 5/11
Parallel.Invoke, Exceptions: 333/1000

Try it on Fiddle.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
0

I'm trying to find a good way of phrasing it; but they are not the same thing.

The reason is, Invoke works on an Array of Action and ForEach works on a List (specifically an IEnumerable) of Action; Lists are significantly different to Arrays in mechanics although they expose the same sort of basic behaviour.

I can't say what the difference actually entails because I don't know, so please don't accept this an an answer (unless you really want too!) but I hope it jogs someones memory as to the mechanisms.

+1 for a good question too.

Edit; It just occurred to me that there is another answer too; Invoke can work on a dynamic list of Actions; but Foreach can work with a Generic IEnumerable of Actions and gives you the ability to use conditional logic, Action by Action; so you could test a condition before saying Action.Invoke() in each Foreach iteration.

Russ Clarke
  • 17,511
  • 4
  • 41
  • 45
  • Thanks for the reply, Russ. I understand the difference in terms of method signatures, I was just wondering if internally they are both using the same approach to chew through the work. – Brad Gagne Jun 03 '12 at 02:39
  • 1
    In that case; no they aren't. ForEach gives you access to the Action before it's invoked; Invoke just does it. – Russ Clarke Jun 03 '12 at 02:44