Parallel.Invoke 和 Parallel.ForEach 本质上是一样的吗?
Posted
技术标签:
【中文标题】Parallel.Invoke 和 Parallel.ForEach 本质上是一样的吗?【英文标题】:Are Parallel.Invoke and Parallel.ForEach essentially the same thing? 【发布时间】:2012-06-07 16:50:01 【问题描述】:我所说的“相同的事情”是指这两个操作基本上做相同的工作,归结为根据您必须使用的内容调用哪个更方便? (即代表列表或要迭代的事物列表)?我一直在搜索 MSDN、*** 和各种随机文章,但我还没有找到明确的答案。
编辑: 我应该更清楚;我在问这两种方法是否做同样的事情,因为如果没有,我想知道哪种方法更有效。
示例:我有一个包含 500 个键值的列表。目前我使用foreach
循环遍历列表(串行)并为每个项目执行工作。如果我想利用多核,我应该直接使用Parallel.ForEach
吗?
假设为了争论,我有一个包含 500 个代表的数组来处理这 500 个任务 - 调用 Parallel.Invoke
并给它一个包含 500 个代表的列表,最终效果会不会有所不同?
【问题讨论】:
【参考方案1】:Parallel.ForEach
遍历元素列表,可以对数组元素执行一些任务。
例如。
Parallel.ForEach(val, (array) => Sum(array));
Parallel.Invoke
可以并行调用多个函数。
例如。
Parallel.Invoke(
() => doSum(array),
() => doAvg(array),
() => doMedian(array));
从上面的示例中,您可以看到它们在功能上有所不同。 ForEach
遍历 List
元素并在每个元素上并行执行一个任务,而Invoke
可以执行许多任务在单个元素上并行。
【讨论】:
"Parallel.ForEach ... 可以对数组的元素执行一些任务。"那么如果我的任务是调用一个函数呢?与 Parallel.Invoke 在工作如何完成以及一个是否会优于另一个方面有什么真正的区别? 1.Parallel.ForEach(val, (array) => Sum(array));
正在调用函数 Sum()
,所以是的,它可以调用函数。 2. 我认为这取决于你需要做什么。如果您在元素列表上调用单个函数而不是 Parallel.ForEach
似乎是一个不错的选择,但如果您要调用多个元素或单个元素的 List
上的一个函数而不是 Parallel.Invoke
将工作得最好。【参考方案2】:
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
【讨论】:
谢谢plukich。在我的特殊情况下,我有一些“即发即弃”的工作要执行,所以我不关心返回值,甚至不关心工作的执行顺序。所以我纯粹好奇是否有任何区别/好处打电话给一个或另一个。 根据我的经验,它们的行为相同。这可能比您想要的要多,但是如果您下载最新的 .NET Reflector 并打开 mscorlib.dll,您可以通过查看 System.Threading.Tasks 中的 Parallel 类来查看问题的确切答案。这就是我会做的。【参考方案3】:令人惊讶的是,不,它们不是一回事。它们的根本区别在于它们在异常情况下的行为方式:
Parallel.ForEach
(以及Parallel.For
和即将推出的Parallel.ForEachAsync
)快速失败。发生异常后,它不会启动任何新的并行工作,并且会在所有当前运行的委托完成后立即返回。
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
Try it on Fiddle.
【讨论】:
【参考方案4】:我正在努力寻找一种好的措辞方式;但它们不是一回事。
原因是,Invoke 作用于 Action 数组,而 ForEach 作用于 List(特别是 IEnumerable)的 Action;列表在机制上与数组有很大不同,尽管它们具有相同的基本行为。
我不能说 差异实际上意味着什么,因为我不知道,所以请不要接受这个答案(除非你真的想要!)但我希望它慢跑有人对机制的记忆。
+1 也是一个好问题。
编辑;我突然想到还有另一个答案; Invoke 可以处理动态的 Action 列表;但是 Foreach 可以使用通用的 IEnumerable 操作,并让您能够使用条件逻辑,逐个操作;所以你可以在每次 Foreach 迭代中说 Action.Invoke() 之前测试一个条件。
【讨论】:
感谢您的回复,拉斯。我理解方法签名方面的差异,我只是想知道它们是否在内部都使用相同的方法来仔细研究工作。 在这种情况下;不,他们不是。 ForEach 使您可以在调用 Action 之前访问它; Invoke 就是这么做的。以上是关于Parallel.Invoke 和 Parallel.ForEach 本质上是一样的吗?的主要内容,如果未能解决你的问题,请参考以下文章