Async在linq select中等待

我需要修改一个现有的程序,它包含以下代码:

var inputs = events.Select(async ev => await ProcessEventAsync(ev)) .Select(t => t.Result) .Where(i => i != null) .ToList(); 

但是这对我来说似乎很奇怪,首先在select中使用asyncawait 。 根据Stephen Cleary的这个答案 ,我应该可以放弃这些。

然后Select第二个Select结果。 这是不是意味着这个任务不是asynchronous的,并且是同步执行的(这么多努力),或者这个任务是asynchronous执行的,而当它完成时,执行其余的查询。

我应该按照斯蒂芬·克里里的另一个答案来写下面的代码:

 var tasks = await Task.WhenAll(events.Select(ev => ProcessEventAsync(ev))); var inputs = tasks.Where(result => result != null).ToList(); 

这是完全一样的吗?

 var inputs = (await Task.WhenAll(events.Select(ev => ProcessEventAsync(ev)))) .Where(result => result != null).ToList(); 

虽然我在这个项目上工作,但我想改变第一个代码示例,但我并不太热衷于改变(exception工作)的asynchronous代码。 也许我只是担心没有和所有3个代码样本做同样的事情?

ProcessEventsAsync看起来像这样:

 async Task<InputResult> ProcessEventAsync(InputEvent ev) {...} 
 var inputs = events.Select(async ev => await ProcessEventAsync(ev)) .Select(t => t.Result) .Where(i => i != null) .ToList(); 

但是这对我来说似乎很奇怪,首先在select中使用asynchronous和等待。 根据Stephen Cleary的这个答案,我应该可以放弃这些。

Select的呼叫有效。 这两条线基本相同:

 events.Select(async ev => await ProcessEventAsync(ev)) events.Select(ev => ProcessEventAsync(ev)) 

(关于如何从ProcessEventAsync抛出同步exception,有一点小小的区别,但在这个代码的上下文中根本就没有关系。)

然后select第二个select结果。 这是不是意味着这个任务不是asynchronous的,并且是同步执行的(这么多努力),或者这个任务是asynchronous执行的,而当它完成时,执行其余的查询。

这意味着查询被阻止。 所以它不是非同步的。

打破它:

 var inputs = events.Select(async ev => await ProcessEventAsync(ev)) 

将首先为每个事件启动一个asynchronous操作。 那么这一行:

  .Select(t => t.Result) 

将等待这些操作一次完成一个(首先等待第一个事件的操作,然后是下一个,然后是下一个等)。

这是我不关心的部分,因为它会阻塞并且会在AggregateException包装任何exception。

这是完全一样的吗?

 var tasks = await Task.WhenAll(events.Select(ev => ProcessEventAsync(ev))); var inputs = tasks.Where(result => result != null).ToList(); var inputs = (await Task.WhenAll(events.Select(ev => ProcessEventAsync(ev)))) .Where(result => result != null).ToList(); 

是的,这两个例子是等价的。 它们都启动所有asynchronous操作( events.Select(...) ),然后asynchronous等待所有操作以任意顺序完成( await Task.WhenAll(...) ),然后继续执行其余的工作( Where... )。

这两个例子都不同于原来的代码。 原始代码被阻塞,并将在AggregateException包装exception。

现有的代码正在工作,但阻止线程。

 .Select(async ev => await ProcessEventAsync(ev)) 

为每个事件创build一个新的任务,但是

 .Select(t => t.Result) 

阻止等待每个新任务的线程结束。

另一方面,你的代码产生相同的结果,但保持asynchronous。

只有一个评论你的第一个代码。 这条线

 var tasks = await Task.WhenAll(events... 

将产生一个单一的任务,所以variables应该以单数命名。

最后你的最后一个代码是相同的,但更简洁

供参考: Task.Wait / Task.WhenAll

使用Linq中现有的方法,看起来相当难看:

  var tasks = items.Select( async item => new { Item = item, IsValid = await IsValid(item) }); var tuples = await Task.WhenAll(tasks); var validItems = tuples .Where(p => p.IsValid) .Select(p => p.Item) .ToList(); 

希望以下版本的.NET将拿出更加优雅的工具来处理集合的任务和集合任务。