Async在linq select中等待
我需要修改一个现有的程序,它包含以下代码:
var inputs = events.Select(async ev => await ProcessEventAsync(ev)) .Select(t => t.Result) .Where(i => i != null) .ToList();
但是这对我来说似乎很奇怪,首先在select中使用async
和await
。 根据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将拿出更加优雅的工具来处理集合的任务和集合任务。