await Task(ReadFromIO) 和 await Task.WhenAll(task1,task2);之间的
本文关键字:await Task task2 之间 WhenAll ReadFromIO task1 | 更新日期: 2023-09-27 17:57:06
我在书中读到了以下的区别。
private async Task GetDataAsync() {
var task1 = ReadDataFromIOAsync();
var task2 = ReadDataFromIOAsync();
// Here we can do more processing
// that doesn't need the data from the previous calls.
// Now we need the data so we have to wait
await Task.WhenAll(task1, task2);
// Now we have data to show.
lblResult.Content = task1.Result;
lblResult2.Content = task2.Result;
}
private async Task GetDataAsync() {
var task1 = ReadDataFromIOAsync();
var task2 = ReadDataFromIOAsync();
lblResult.Content = await task1;
lblResult2.Content = await task2;
}
我了解了第一种方法的 await 语句中发生了什么。但是对于第二个,虽然我理解了逻辑,但我无法理解第二个实现与第一个实现相比的陷阱。在书中,他们提到编译器重写了该方法两次。我的理解是,由于两个等待调用,由于我们在这里为每个任务分别调用 await,因此可能会比第一个有更多的时间延迟。有人可以用更好的方式解释我吗?
我不知道
你的书想表达什么观点,但我同意你最初对它的互操作的猜测。
可能存在的问题在于,如果处理时间超过 task1
,则可能存在一段时间lblResult
显示新数据,lblResult2
显示旧数据task2
。在第一种方法中,您等到两个任务完成,然后同时更新两个标签(当您退出方法时,两个标签会同时在屏幕上重新绘制)。在第二种方法中,您更新第一个标签,然后为消息循环提供重新绘制屏幕的机会,然后一段时间后更新第二个标签并在屏幕上更新该值。
我想对于第二个示例,您也会有一个稍微复杂的状态机,但开销可以忽略不计,我相信这本书试图指出您和我都提出的问题。
本质上,第一种方法的作用是这样的:
- 开始
task1
- 开始
task2
- 异步等待
task1
和task2
完成
第二个实现这样做:
- 开始
task1
- 开始
task2
- 异步等待
task1
完成。 -
task1
完成后,恢复执行,然后异步等待task2
完成。
因此,使用第二种方法,您可以单独等待每个任务的结果,而不是等待两个任务完成。如果task1
在task2
之前完成,代码将恢复执行,然后立即返回,这将导致额外的上下文切换,这可能需要额外的时间。同样,对于多个等待的情况,编译器最终可能会生成更复杂的状态机,但其影响应该可以忽略不计。
无论哪种情况,在两个任务完成之前,您都不会使用结果,因此应用程序的行为不应有太大差异。