await Task(ReadFromIO)和await Task.WhenAll(task1,task2)之间的区别;

时间:2014-04-06 22:32:03

标签: c# async-await

我在书中读到了下面的差异。

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语句中发生了什么。但对于第二个,虽然我理解逻辑我无法理解第二个实现与第一个实现相比的缺陷。在书中,他们提到编译器会重写该方法两次。我理解的是因为两个等待呼叫,可能会有比第一个更多的时间延迟,因为我们在这里单独调用等待每个任务。有人可以用更好的方式解释我吗?

2 个答案:

答案 0 :(得分:1)

我不知道你的书想要做什么,但我同意你对它的互操作的初步猜测。

问题可能是lblResult显示新数据可能会有一段时间,如果lblResult2需要的时间超过task2,则task1会显示旧数据。在第一种方法中,您等待两个任务完成,然后同时更新两个标签(当您退出方法时,两者同时在屏幕上重新绘制)。在第二种方法中,您更新第一个标签,然后为消息循环提供重新绘制屏幕的机会,然后一段时间后更新第二个标签并在屏幕上更新该值。

我猜你的第二个例子也会有一个稍微更复杂的状态机,但是开销可以忽略不计,我相信这本书试图指出你和我这两个问题想出来了。

答案 1 :(得分:1)

基本上第一种方法是做什么的:

  1. 开始task1
  2. 开始task2
  3. 异步等待task1 task2完成
  4. 第二个实现是这样做的:

    1. 开始task1
    2. 开始task2
    3. 异步等待task1完成。
    4. task1完成后,继续执行,然后异步等待task2完成。
    5. 因此,使用第二种方法,您将单独等待每项任务的结果,而不是等待两项任务完成。在task1task2之前完成的情况下,代码将继续执行然后立即返回,这将导致额外的上下文切换,这可能需要额外的时间。此外,对于多个等待的情况,编译器可能最终生成更复杂的状态机,但其影响应该可以忽略不计。

      在任何一种情况下,在两个任务完成之前你都没有使用结果,所以应用程序的行为不应该太不相同。