LINQ代码中的异步-澄清
本文关键字:澄清 异步 代码 LINQ | 更新日期: 2023-09-27 18:03:31
几乎每个SO关于这个主题的回答都指出:
LINQ不能与异步完美配合
还有:
我建议您不要将此视为"在LINQ中使用异步">
但在斯蒂芬的书中有一个例子:
问题:您有一系列任务要等待,并且您想做一些在每个任务完成后对其进行处理。但是,你想每个的处理一旦完成,而不是等待任何其他任务。
其中一个推荐的解决方案是:
static async Task<int> DelayAndReturnAsync(int val)
{
await Task.Delay(TimeSpan.FromSeconds(val));
return val;
}
// This method now prints "1", "2", and "3".
static async Task ProcessTasksAsync()
{
// Create a sequence of tasks.
Task<int> taskA = DelayAndReturnAsync(2);
Task<int> taskB = DelayAndReturnAsync(3);
Task<int> taskC = DelayAndReturnAsync(1);
var tasks = new[] { taskA, taskB, taskC };
var processingTasks = tasks.Select(async t =>
{
var result = await t;
Trace.WriteLine(result);
}).ToArray();
// Await all processing to complete
await Task.WhenAll(processingTasks);
}
问题1:
我不明白为什么现在LINQ语句中的async
确实有效。我们不是说"不要考虑在LINQ中使用async
"吗?
问题2:
当控件到达此处的await t
时,实际发生了什么?控件是否离开ProcessTasksAsync
方法?还是离开匿名方法继续迭代?
我不明白为什么现在LINQ语句中的async确实有效。我们不是说"不要考虑在LINQ中使用异步"吗?
async
大多不适用于LINQ,因为IEnumerable<T>
扩展并不总是正确地推断委托类型,并遵循Action<T>
。他们对Task
类没有特别的了解。这意味着实际的异步委托变成了async void
,这很糟糕。在Enumerable.Select
的情况下,我们有一个重载,它返回一个Func<T>
(在我们的情况下将是Func<Task>
(,这相当于async Task
,因此它适用于异步用例。
当控件到达等待t时,实际发生了什么?控件是否离开ProcessTasksAsync方法?
不,不是。CCD_ 14是关于投影序列中的所有元素。这意味着,对于集合中的每个元素,await t
将把控制权交还给迭代器,迭代器将继续迭代所有元素。这就是为什么以后必须使用await Task.WhenAll
,以确保所有元素都已完成执行。
问题1:
不同的是,每个任务都继续,并进行额外的处理,即:Trace.WriteLine(result);
。在您指向的链接中,该代码不会更改任何内容,只会产生等待和包装另一个任务的开销。
问题2:
当控件到达等待t时,实际发生了什么?
它等待ProcessTasksAsync
的任务结果,然后继续执行Trace.WriteLine(result);
。当我们得到结果并且处理仍在匿名方法中时,我们可以说控件离开了ProcessTasksAsync
方法。
最后,我们有await Task.WhenAll(processingTasks);
,它将等待包括额外处理(Trace.WriteLine(result);
(在内的所有任务在继续之前完成,但每个任务都不等待其他任务继续执行:Trace.WriteLine(result);
这样会更好:
static async Task<int> DelayAndReturnAsync(int val)
{
await Task.Delay(TimeSpan.FromSeconds(val));
return val;
}
static async Task AwaitAndProcessAsync(Task<int> task)
{
var result = await task;
Console.WriteLine(result);
}
// This method now prints "1", "2", and "3".
static async Task ProcessTasksAsync()
{
// Create a sequence of tasks.
Task<int> taskA = DelayAndReturnAsync(2);
Task<int> taskB = DelayAndReturnAsync(3);
Task<int> taskC = DelayAndReturnAsync(1);
var tasks = new[] { taskA, taskB, taskC };
var processingTasks = tasks.Select(AwaitAndProcessAsync).ToArray();
// Await all processing to complete
await Task.WhenAll(processingTasks);
}
任务的数组,因为AwaitAndProcessAsync返回Task。