我目前正在阅读" 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
,因为任务已经完成了?
答案 0 :(得分:129)
这里已经有了一些很好的答案/评论,但只是为了报名......
我更倾向于await
而不是Result
(或Wait
),这有两个原因。首先是错误处理是不同的; await
不会将异常包装在AggregateException
中。理想情况下,异步代码根本不应该处理AggregateException
,除非它特意想要。
第二个原因是更微妙。正如我在我的博客(以及书中),Result
/Wait
can cause deadlocks和can cause even more subtle deadlocks when used in an async
method中所描述的那样。因此,当我通过代码阅读并看到Result
或Wait
时,这是一个即时警告标志。 Result
/ Wait
只有在完全确定任务已完成时才是正确的。这不仅难以一目了然(在实际代码中),而且代码更改也更加脆弱。
这并不是说Result
/ Wait
永远不会使用。我在自己的代码中遵循这些指南:
await
。Result
/ Wait
。这种用法应该有评论。Result
和Wait
。请注意,(1)是迄今为止常见的情况,因此我倾向于在任何地方使用await
并将其他情况视为一般规则的例外。
答案 1 :(得分:9)
如果timeoutTask
是Task.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 downloadTask
和return downloadTask.Result
之间的差异非常小。正如@KirillShlenskiy在评论中所指出的那样,后者会抛出AggregateException
来包装任何原始异常。前者只会重新抛出原始异常。
在任何一种情况下,无论何处处理异常,都应检查AggregateException
及其内部异常,以找出错误原因。