C# Parallel.ForEach,异步lambda等待所有迭代完成

C# Parallel.ForEach,异步lambda等待所有迭代完成,c#,task-parallel-library,parallel.foreach,C#,Task Parallel Library,Parallel.foreach,最近,我看到几个与Parallel.ForEach相关的SO线程与异步lambdas混合在一起,但所有建议的答案都是某种解决方法 有没有办法让我写下: List<int> list = new List<int>[](); Parallel.ForEach(arrayValues, async (item) => { var x = await LongRunningIoOperationAsync(item); list.Add(x); }); Lis

最近,我看到几个与Parallel.ForEach相关的SO线程与异步lambdas混合在一起,但所有建议的答案都是某种解决方法

有没有办法让我写下:

List<int> list = new List<int>[]();

Parallel.ForEach(arrayValues, async (item) =>
{
  var x = await LongRunningIoOperationAsync(item);
  list.Add(x);
});
List List=新列表[]();
Parallel.ForEach(数组值,异步(项)=>
{
var x=等待LongRunningIoOperationAsync(项目);
增加(x);
});
如何确保列表将包含在每次迭代中使用lambdas执行的所有迭代中的所有项

Parallel.ForEach通常如何与异步lambdas一起工作,如果它点击wait,它会将其线程移交给下一个迭代吗


我假设ParallelLoopResult IsCompleted字段不是正确的字段,因为当所有迭代都执行时,它将返回true,无论它们的实际lambda作业是否完成?

您不需要
并行。对于/ForEach
,您只需要等待任务列表

背景

简而言之,您需要非常小心异步lambda,如果要将它们传递给
操作
Func

您的问题是因为
Parallel.For/ForEach
不适合异步和等待模式或IO绑定任务。它们适合cpu限制的工作负载。这意味着它们基本上具有
操作
参数,让任务调度器为您创建任务

如果您想同时运行多个异步任务,请使用
Task.whalll
,或者可以有效处理CPU绑定和IO绑定工作负载的TPL数据流块(或类似的东西),或者更直接地说,它们可以处理异步方法所指的任务

除非您需要在lambda内部执行更多操作(尚未显示),否则只需使用
Select
whalll

var tasks = items .Select(LongRunningIoOperationAsync);
var results = await Task.WhenAll(tasks); // here is your list of int
如果你这样做了,你仍然可以使用等待

var tasks = items.Select(async (item) =>
   {
       var x = await LongRunningIoOperationAsync(item);
       // do other stuff
       return x;
   });

var results = await Task.WhenAll(tasks);
注意:如果您需要
Parallel.ForEach
的扩展功能(即控制最大并发性的选项),有几种方法,但是RX或数据流可能是最简洁的方法

最近,我看到几个与Parallel.ForEach相关的SO线程与异步lambdas混合在一起,但所有建议的答案都是某种解决方法

这是因为
Parallel
不适用于
async
。从另一个角度来看,你为什么要把它们混合在一起?他们做相反的事情
Parallel
是关于添加线程的,而
async
是关于放弃线程的。如果要同时执行异步工作,请使用
Task.whalll
。这是工作的正确工具<代码>并行不可用

也就是说,听起来你想使用错误的工具,所以这里是你如何做到这一点

如何确保列表将包含在每次迭代中使用lambdas执行的所有迭代中的所有项

您需要有某种类型的信号,某些代码可以阻止该信号,直到处理完成,例如,
CountdownEvent
Monitor
。另外,您还需要保护对非线程安全的
列表的访问

Parallel.ForEach通常如何与异步lambdas一起工作,如果它点击wait,它会将其线程移交给下一个迭代吗

由于
Parallel
不理解
async
lambdas,当第一个
wait
向其调用者屈服(返回)时,
Parallel
将假定循环的交互已完成

我假设ParallelLoopResult IsCompleted字段不是正确的字段,因为当所有迭代都执行时,它将返回true,无论它们的实际lambda作业是否完成


对。就
Parallel
所知,它只能“看到”返回给调用方的第一个
wait
的方法。因此它不知道
async
lambda何时完成。它还将假定迭代完成得太早,这会导致分区延迟。

await将强制释放任务线程,并且它不会保证在等待的任务完成时相同的线程将继续。但是的,除非发生异常,否则并行ForEach将保证所有迭代都已完成,并且x已添加到列表中。您最好使用
var results=wait Task.whalll(arrayvalues.Select(x=>LongRunningIoOperationAsync(x))
。并行更适合CPU绑定的工作,而不是IO绑定的工作。您的实现不是线程安全的。如果要限制并发异步操作的数量,则不能调用
列表。在
Parallel.ForEach
@ldragicevic中添加(x)
。谢谢,我有点好奇,只有当c#程序可以处理任意长度的项目时,例如,如果我通过1000个项目,那么安排一些有限数量的线程,以较小数量的线程成批执行所有1000个任务是否明智threads@ldragicevic默认任务计划程序将根据工作负载限制并发性,响应的核心和启发式感谢。我只是担心是否需要处理并行性的资源,但根据前面的评论,这没关系。总是读得好,写得好。请@ldragicevic注意,您接受的投票应该在这里是的,当然@斯蒂芬非常感谢你的澄清!把这些事实牢记在心是完全有道理的。