23

“相同的事情”我的意思是这两个操作基本上做同样的工作,它只是归结为根据你必须使用的内容调用哪个更方便?(即代表列表或要迭代的事物列表)?我一直在搜索 MSDN、StackOverflow 和各种随机文章,但我还没有找到明确的答案。

编辑: 我应该更清楚;我在问这两种方法是否做同样的事情,因为如果他们不这样做,我想了解哪种方法更有效。

示例:我有一个包含 500 个键值的列表。目前我使用一个foreach循环遍历列表(串行)并为每个项目执行工作。如果我想利用多个核心,我应该简单地使用Parallel.ForEach吗?
假设为了争论,我有一个包含 500 个代表的数组来处理这 500 个任务 - 最终效果会是任何不同的调用Parallel.Invoke并给它一个包含 500 个代表的列表吗?

4

4 回答 4

32

Parallel.ForEach遍历元素列表并可以对数组的元素执行一些任务。

例如。

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

Parallel.Invoke可以并行调用许多函数。

例如。

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

从上面的示例中,您可以看到它们在功能上有所不同。ForEach遍历一个List元素并在每个元素上并行执行一个任务,同时可以在单个元素上并行执行多个任务Invoke

于 2012-06-03T02:18:10.530 回答
11

Parallel.Invoke 和 Parallel.ForEach(当用于执行操作时)功能相同,但确实有人特别希望集合是一个数组。考虑以下示例:

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();

此代码在一次运行时产生此输出。它的输出通常每次都以不同的顺序排列。

0 5 1 6 2 7 3 8 4 9

0 1 2 4 5 6 7 8 9 3

于 2012-06-03T02:20:08.330 回答
1

令人惊讶的是,不,它们不是一回事。它们的根本区别在于它们在异常情况下的行为方式:

  1. the Parallel.ForEach(以及 theParallel.For和即将到来的Parallel.ForEachAsync快速失败。发生异常后,它不会启动任何新的并行工作,并且会在所有当前运行的委托完成后立即返回。
  2. 总是调用所有的Parallel.Invoke动作,不管它们中的一些(或全部)是否失败。

为了演示这种行为,让我们并行运行 1,000 个操作,每三个操作有一个失败:

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}"); }

输出(在我的 PC 中,.NET 5,发布版本):

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

在 Fiddle 上试试

于 2021-09-12T15:39:38.057 回答
0

我正在尝试找到一种很好的措辞方式;但它们不是一回事。

原因是,Invoke 作用于 Action 数组,而 ForEach 作用于 Action 列表(特别是 IEnumerable);列表在机制上与数组有很大不同,尽管它们具有相同的基本行为。

我不能说实际上有什么区别,因为我不知道,所以请不要接受这个答案(除非你也真的想要!)但我希望它可以唤起人们对机制的记忆。

+1也是一个好问题。

编辑; 我突然想到还有另一个答案;Invoke 可以处理动态的 Action 列表;但是 Foreach 可以使用通用的 IEnumerable 操作,并让您能够使用条件逻辑,逐个操作;所以你可以在每次 Foreach 迭代中说 Action.Invoke() 之前测试一个条件。

于 2012-06-03T01:57:21.333 回答