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


117

我目前正在阅读Stephen Cleary撰写的“ C#Cookbook中的并发性 ”,并且注意到以下技术:

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

downloadTask是对的调用httpclient.GetStringAsynctimeoutTask正在执行Task.Delay

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


3
这里缺少上下文,除非人们容易获得这本书,否则您将需要将其包括在内。什么是downloadTasktimeoutTask?他们在做什么?
Mike Perrenoud 2014年

7
我在这里没有看到成功完成的实际检查。任务很可能是有故障的,在这种情况下,行为(不同AggregateExceptionResult通过VS第一异常ExceptionDispatchInfoawait)。在Stephen Toub的“ .NET 4.5中的任务异常处理”中有更详细的讨论:blogs.msdn.com/b/pfxteam/archive/2011/09/28/…
Kirill Shlenskiy 2014年

您应该将其作为答案@KirillShlenskiy
卡斯滕

@MichaelPerrenoud您说得对,谢谢您,我将编辑问题。
julio.g 2014年

Answers:


160

这里已经有一些不错的答案/评论,但是只是为了提醒...

有两个原因为什么我喜欢awaitResult(或Wait)。首先是错误处理不同;await不会将异常包装在中AggregateException。理想情况下,异步代码根本不需要处理AggregateException,除非它特别想要处理。

第二个原因是更微妙的。正如我在博客(以及本书)中所描述的那样,Result/ Wait可能导致死锁,并且async方法中使用时可能会导致更细微的死锁。因此,当我阅读代码并看到Result或时Wait,这是立即警告标志。的Result/ Wait如果你是唯一正确的绝对肯定的任务已经完成。这不仅一目了然(在现实世界的代码中),而且对代码的更改也更加脆弱。

这并不是说Result/ Wait应该永远不会被使用。我在自己的代码中遵循以下准则:

  1. 应用程序中的异步代码只能使用await
  2. 异步实用程序代码(在库中)偶尔可以使用Result/(Wait如果该代码确实需要)。这种用法可能应该有注释。
  3. 并行任务代码可以使用ResultWait

请注意,(1)到目前为止是最常见的情况,因此,我倾向于在await所有地方使用并将其他情况视为一般规则的例外。


我们在项目中使用“结果”而不是“等待”遇到了死锁。混乱的部分没有编译错误,并且一段时间后您的代码变得不稳定。
艾哈迈德·穆萨维

@Stephen,请您解释一下为什么“理想情况下,异步代码根本不需要处理AggregateException,除非它特别想要处理”
vcRobe

4
@vcRobe因为await防止AggregateException包装。AggregateException设计用于并行编程,而不是异步编程。
史蒂芬·克雷里

2
>“只有在完全确定任务已经完成的情况下,等待才是正确的。” ....那为什么叫等待呢?
Ryan The Leach

4
@RyanTheLeach:的最初目的Wait是加入动态任务并行性 Task实例。使用它等待异步Task实例很危险。微软考虑引入一种新的“ Promise”类型,但选择使用现有的类型TaskTask为异步任务重用现有类型的权衡是,您最终会获得几个API,这些API根本不应该在异步代码中使用。
Stephen Cleary

12

如果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.ResultAggregateException就像@KirillShlenskiy在评论中指出的那样,后者将引发包装任何原始异常的情况。前者只是重新抛出原始异常。

无论哪种情况,无论您在哪里处理异常,都应该检查AggregateException其内部异常,以查明错误原因。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.