等待一个完成的任务相同的task.Result?
我目前正在阅读Stephen Cleary的“C#Cookbook中的并发”,并且我注意到了以下技术:
var completedTask = await Task.WhenAny(downloadTask, timeoutTask); if (completedTask == timeoutTask) return null; return await downloadTask;
downloadTask是对httpclient.GetStringAsync的调用,并且timeoutTask正在执行Task.Delay。
如果没有超时,那么downloadTask已经完成。 为什么有必要做一秒钟的等待,而不是返回downloadTask.Result,鉴于任务已经完成?
这里已经有一些很好的答案/评论,但只是为了提醒…
有两个原因,我更喜欢await
Result
(或Wait
)。 首先是error handling是不同的; await
不会在AggregateException
包装exception。 理想情况下,asynchronous代码根本不需要处理AggregateException
,除非特别需要。
第二个原因是更微妙一点。 正如我在我的博客(和本书)中所描述的那样, Result
/ Wait
会导致死锁 ,并且在async
方法中使用时会导致更微妙的死锁 。 所以,当我通过代码阅读,并看到一个Result
或Wait
,这是一个即时的警告标志。 如果您完全确定任务已完成, Result
/ Wait
才是正确的。 不仅难以一眼就看出(在真实世界的代码中),而且编码变化也更加脆弱。
这并不是说不应该使用Result
/ Wait
。 我在我自己的代码中遵循这些准则:
- 应用程序中的asynchronous代码只能使用
await
。 - 如果代码真的需要,asynchronous实用程序代码(在库中)偶尔会使用
Result
/Wait
。 这种用法应该可能有意见。 - 并行任务代码可以使用
Result
和Wait
。
请注意,(1)是常见的情况,因此,我倾向于使用,并将其他情况视为一般规则的例外情况。
这是有道理的,如果timeoutTask
是timeoutTask
的产品,我相信这是书中的内容。
Task.WhenAny
返回Task<Task>
,其中内部任务是您作为parameter passing的任务之一。 它可以像这样重写:
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
之间有一个很小的区别。 这是因为后者将抛出AggregateException
,它包装了任何原始exception,正如@KirillShlenskiy在注释中指出的那样。 前者会重新抛出原来的exception。
在任何情况下,无论您处理exception,都应该检查AggregateException
及其内部exception,以找出错误的原因。
- 在asynchronous方法结束时,我应该返回还是等待?
- 尝试/ catchasynchronous/等待块
- asynchronous是否等待关键字相当于一个ContinueWith lambda?
- 你必须把Task.Run放在一个方法来使其asynchronous?
- .Net 4.5中Async / Await的简要说明
- 为什么在等待后HttpContext.Current为空?
- StaTaskScheduler和STA线程消息抽取
- 在.NET 4.0中创build一个可以与.NET 4.5中的“await”一起使用的asynchronous方法
- asynchronous函数+ await + setTimeout的组合