2016-01-29 98 views
1

经过几个小时的挣扎之后,我在我的应用程序中发现了一个错误。我认为下面的两个函数具有相同的行为,但事实证明它们没有。Task.WhenAll()和foreach(任务中的var任务)之间的差异

任何人都可以告诉我什么是真正发生在引擎盖下,为什么他们的行为以不同的方式?

public async Task MyFunction1(IEnumerable<Task> tasks){ 
    await Task.WhenAll(tasks); 

    Console.WriteLine("all done"); // happens AFTER all tasks are finished 
} 

public async Task MyFunction2(IEnumerable<Task> tasks){ 
    foreach(var task in tasks){ 
     await task; 
    } 

    Console.WriteLine("all done"); // happens BEFORE all tasks are finished 
} 
+4

“发生之前的所有任务完成”看上去很奇怪 - 你可以有[MCVE],显示这种情况?我没有看到为什么“foreach”会按照您在等待所有任务时描述的方式行事(可能不如“WhenAll”最佳,但仍然全部)。 –

+0

该代码甚至没有编译。 'return'在哪里? –

+0

'WhenAll'和在一个循环中手动等待它们的一般区别在于,后者将不断地来回切换到异步方法,需要大量的上下文切换,而前者在内部等待它们而没有那开销。 – poke

回答

6

他们将同样发挥作用如果所有任务成功完成

如果使用WhenAll任何项目失败,它仍然无法完成,直到所有项目完成后,它会代表AggregatException一个包装所有错误的所有任务。

如果await每一个那么它很快就会为它击中任何项目失败完成,它会表现的异常为一个错误,而不是任何其他人。


两个也不同之处在于WhenAll一定会实现整个IEnumerable在一开始,添加任何延续到其他项目之前。如果IEnumerable表示已存在和已启动的任务的集合,则这不是相关的,但是如果迭代可枚举的行为创建和/或启动任务,则在开始时实现序列将全部并行运行,以及在提取下一个任务之前等待每个任务将按顺序执行它们。下面是一个IEnumerable你可以传递,由于我这里描述的将表现:

public static IEnumerable<Task> TaskGeneratorSequence() 
{ 
    for(int i = 0; i < 10; i++) 
     yield return Task.Delay(TimeSpan.FromSeconds(2); 
} 
+0

感谢您的回答。假如没有例外情况发生,那么可能导致我描述的情况是什么? – gisek

+0

@gisek取消或迭代时创建任务的输入序列。 – Servy