我最近在阅读一些使用很多异步方法的代码,但是有时需要同步执行它们。该代码可以:
Foo foo = GetFooAsync(...).GetAwaiter().GetResult();
这和
Foo foo = GetFooAsync(...).Result;
async
/ await
方法)
我最近在阅读一些使用很多异步方法的代码,但是有时需要同步执行它们。该代码可以:
Foo foo = GetFooAsync(...).GetAwaiter().GetResult();
这和
Foo foo = GetFooAsync(...).Result;
async
/ await
方法)
Answers:
差不多了 但是有一个小的区别:如果Task
失败,GetResult()
将仅引发直接导致的异常,而Task.Result
将引发AggregateException
。但是,在使用它们时有async
什么意义呢?最好使用100倍await
。
另外,您也不打算使用GetResult()
。它只供编译器使用,不适合您。但是,如果您不想烦人AggregateException
,请使用它。
async Task
单元测试,并且已经有一段时间了。
The 100x better option is to use await.
我讨厌这样的陈述,如果我能await
在它前面打耳光,我会。但是,当我试图让异步代码来对抗像什么经常发生在我身上的非异步代码工作了很多在Xamarin,我最终不得不用的东西一样ContinueWith
不少,以使其不死锁的UI。编辑:我知道这很老了,但是这并不能减轻我的沮丧感,因为对于不能仅仅使用的情况,没有其他选择的答案await
。
Task.GetAwaiter().GetResult()
优先于Task.Wait
,Task.Result
因为它传播异常而不是将异常包装在AggregateException
。但是,这三种方法都可能导致死锁和线程池不足的问题。应该避免他们都赞成async/await
。
下面的引用解释了为什么Task.Wait
并且Task.Result
不仅仅包含Task.GetAwaiter().GetResult()
(由于“非常高的兼容性”)的异常传播行为。
正如我之前提到的,我们有一个很高的兼容性标准,因此我们避免了改动。因此,
Task.Wait
保留了始终包装的原始行为。但是,您可能会发现自己处在某些高级情况下,这些情况下您想要的行为类似于所采用的同步阻塞Task.Wait
,但是您希望将原始异常展开而不是传播,而不是将其封装在中AggregateException
。为此,您可以直接定位任务的等待者。当您编写“await task;
”时,编译器Task.GetAwaiter()
会将其转换为方法的用法,这将返回具有GetResult()
方法的实例。当用于有故障的任务时,GetResult()
将传播原始异常(这是“await task;
” 如何获得其行为)。因此,您可以使用“task.GetAwaiter().GetResult()
如果您想直接调用此传播逻辑。
https://blogs.msdn.microsoft.com/pfxteam/2011/09/28/task-exception-handling-in-net-4-5/
“
GetResult
”实际上表示“检查任务是否有错误”通常,我会尽最大努力避免异步阻塞异步任务。但是,在少数情况下,我确实违反了该准则。在那些罕见的情况下,我的首选方法是
GetAwaiter().GetResult()
因为它保留了任务异常,而不是将它们包装在中AggregateException
。
http://blog.stephencleary.com/2014/12/a-tour-of-task-part-6-results.html
Task.GetAwaiter().GetResult()
等同于await task
。我假设在无法用方法标记方法async
(例如构造函数)时使用第一个选项。那是对的吗?如果是,那么它就会与最高答案冲突@ It'sNotALie
Task.GetAwaiter().GetResult()
与Task.Wait
和更等效Task.Result
(因为这三个都将同步阻塞并可能产生死锁),但是Task.GetAwaiter().GetResult()
具有await任务的异常传播行为。
https://github.com/aspnet/Security/issues/59
“最后一句话:您应该避免使用
Task.Result
并且Task.Wait
尽可能地避免它们总是将内部异常封装在中,AggregateException
并用通用的消息(发生一个或多个错误)替换消息,这会使调试更加困难。即使同步版本不应该不会经常使用,您应该强烈考虑Task.GetAwaiter().GetResult()
改为使用。”
Task.WhenAll(task1, task2).GetAwaiter().GetResult();
。
另一个区别是当async
函数返回Task
而不是时,Task<T>
您不能使用
GetFooAsync(...).Result;
鉴于
GetFooAsync(...).GetAwaiter().GetResult();
仍然有效。
我知道问题中的示例代码适合这种情况Task<T>
,但是问题通常被问到。
Result
,GetIntAsync()
而返回的结果Task<int>
不只是Task
。我建议您再次阅读我的答案。
GetFooAsync(...).Result
在return的函数内部Task
。现在这很有意义,因为C#中没有void属性(Task.Result
是一个属性),但是您当然可以调用void方法。
如前所述,如果可以使用await
。如果您需要像您提到的那样同步运行代码.GetAwaiter().GetResult()
,.Result
或者.Wait()
像许多评论/回答中所说的那样存在死锁的风险。由于我们大多数人都喜欢oneliners,因此您可以将其用于.Net 4.5<
通过异步方法获取值:
var result = Task.Run(() => asyncGetValue()).Result;
同步调用异步方法
Task.Run(() => asyncMethod()).Wait();
使用不会出现死锁问题Task.Run
。
资源:
如果任务失败,那么当连续代码调用awaiter.GetResult()时,将引发异常。无需调用GetResult,我们可以简单地访问任务的Result属性。调用GetResult的好处是,如果任务失败,则将异常直接引发而不将其包装在AggregateException中,从而允许更简单,更干净的catch块。
对于非常规任务,GetResult()的返回值是无效的。这样,它的有用功能就是仅抛出异常。
源:简而言之的c#7.0
GetResult
:“此类型及其成员旨在供编译器使用。” 其他人不应该使用它。