等待与task.Result相同的已完成任务?

时间:2014-07-08 03:13:14

标签: c# asynchronous async-await task

我目前正在阅读" C#Cookbook中的并发"作者:Stephen Cleary,我注意到以下技巧:

var completedTask = await Task.WhenAny(downloadTask, timeoutTask);  
if (completedTask == timeoutTask)  
  return null;  
return await downloadTask;  

downloadTask是对httpclient.GetStringAsync的来电,timeoutTask正在执行Task.Delay

如果它没有超时,则downloadTask已经完成。为什么有必要做第二次等待而不是返回downloadTask.Result,因为任务已经完成了?

2 个答案:

答案 0 :(得分:129)

这里已经有了一些很好的答案/评论,但只是为了报名......

我更倾向于await而不是Result(或Wait),这有两个原因。首先是错误处理是不同的; await不会将异常包装在AggregateException中。理想情况下,异步代码根本不应该处理AggregateException,除非它特意想要

第二个原因是更微妙。正如我在我的博客(以及书中),Result/Wait can cause deadlockscan cause even more subtle deadlocks when used in an async method中所描述的那样。因此,当我通过代码阅读并看到ResultWait时,这是一个即时警告标志。 Result / Wait只有在完全确定任务已完成时才是正确的。这不仅难以一目了然(在实际代码中),而且代码更改也更加脆弱。

这并不是说Result / Wait 永远不会使用。我在自己的代码中遵循这些指南:

  1. 应用程序中的异步代码只能使用await
  2. 如果代码真的需要它,异步实用程序代码(在库中)偶尔可以使用Result / Wait。这种用法应该有评论。
  3. 并行任务代码可以使用ResultWait
  4. 请注意,(1)是迄今为止常见的情况,因此我倾向于在任何地方使用await并将其他情况视为一般规则的例外。

答案 1 :(得分:9)

如果timeoutTaskTask.Delay的产品,这是有道理的,我相信它在书中的含义。

Task.WhenAny返回Task<Task>,其中内部任务是您作为参数传递的任务之一。它可以像这样重写:

Task<Task> anyTask = Task.WhenAny(downloadTask, timeoutTask);
await anyTask;
if (anyTask.Result == timeoutTask)  
  return null;  
return downloadTask.Result; 

在任何一种情况下,由于downloadTask已经完成,return await downloadTaskreturn downloadTask.Result之间的差异非常小。正如@KirillShlenskiy在评论中所指出的那样,后者会抛出AggregateException来包装任何原始异常。前者只会重新抛出原始异常。

在任何一种情况下,无论何处处理异常,都应检查AggregateException及其内部异常,以找出错误原因。