Jay*_*uzi 281 c# async-await
我最近在阅读一些使用大量异步方法的代码,但有时需要同步执行它们.代码确实:
Foo foo = GetFooAsync(...).GetAwaiter().GetResult();
Run Code Online (Sandbox Code Playgroud)
这是一样的吗?
Foo foo = GetFooAsync(...).Result;
Run Code Online (Sandbox Code Playgroud)
?
It'*_*ie. 156
差不多.虽然有一个小的区别:如果Task失败,GetResult()只会抛出直接造成的异常,同时Task.Result会抛出一个AggregateException.但是,使用其中任何一个时有async什么意义呢?100x更好的选择是使用await.
此外,你不打算使用GetResult().它仅适用于编译器,不适合您.但如果你不想讨厌AggregateException,请使用它.
Nit*_*wal 116
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
小智 66
https://github.com/aspnet/Security/issues/59
"最后一句话:你应该尽可能地避免使用
Task.Result和Task.Wait尽可能多的因为它们总是将内部异常封装在一个AggregateException并用通用的替换消息(发生一个或多个错误),这使得调试更加困难.即使同步版本不应该经常使用,你应该强烈考虑使用Task.GetAwaiter().GetResult()."
Nur*_*mir 30
另一个区别是当async函数返回Task而不是Task<T>那时你不能使用
GetFooAsync(...).Result;
Run Code Online (Sandbox Code Playgroud)
而
GetFooAsync(...).GetAwaiter().GetResult();
Run Code Online (Sandbox Code Playgroud)
仍然有效.
我知道问题中的示例代码是针对这种情况的Task<T>,但问题一般是问的.
Ogg*_*las 15
如前所述,如果可以使用await。如果您需要像您提到的那样同步运行代码.GetAwaiter().GetResult(),.Result或者.Wait()像许多评论/回答中所说的那样存在死锁的风险。由于我们大多数人都喜欢oneliners,因此您可以将其用于.Net 4.5<
通过异步方法获取值:
var result = Task.Run(() => asyncGetValue()).Result;
Run Code Online (Sandbox Code Playgroud)
同步调用异步方法
Task.Run(() => asyncMethod()).Wait();
Run Code Online (Sandbox Code Playgroud)
使用不会出现死锁问题Task.Run。
资源:
我检查了TaskOfResult.cs(TaskOfResult.cs的源代码)的源代码:
如果Task没有完成,Task.Result会调用中的Task.Wait()方法getter。
public TResult Result
{
get
{
// If the result has not been calculated yet, wait for it.
if (!IsCompleted)
{
// We call NOCTD for two reasons:
// 1. If the task runs on another thread, then we definitely need to notify that thread-slipping is required.
// 2. If the task runs inline but takes some time to complete, it will suffer ThreadAbort with possible state corruption.
// - it is best to prevent this unless the user explicitly asks to view the value with thread-slipping enabled.
//#if !PFX_LEGACY_3_5
// Debugger.NotifyOfCrossThreadDependency();
//#endif
Wait();
}
// Throw an exception if appropriate.
ThrowIfExceptional(!m_resultWasSet);
// We shouldn't be here if the result has not been set.
Contract.Assert(m_resultWasSet, "Task<T>.Result getter: Expected result to have been set.");
return m_result;
}
internal set
{
Contract.Assert(m_valueSelector == null, "Task<T>.Result_set: m_valueSelector != null");
if (!TrySetResult(value))
{
throw new InvalidOperationException(Strings.TaskT_TransitionToFinal_AlreadyCompleted);
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果我们调用 的GetAwaiter方法Task,Task将包装TaskAwaiter<TResult>(GetAwaiter() 的源代码),(TaskAwaiter 的源代码):
public TaskAwaiter GetAwaiter()
{
return new TaskAwaiter(this);
}
Run Code Online (Sandbox Code Playgroud)
如果我们调用GetResult()的方法TaskAwaiter<TResult>,它会调用Task.Result属性时,Task.Result将调用Wait()的方法Task(调用getResult()的源码):
public TResult GetResult()
{
TaskAwaiter.ValidateEnd(m_task);
return m_task.Result;
}
Run Code Online (Sandbox Code Playgroud)
它是ValidateEnd(Task task)(ValidateEnd(Task task) 的源代码)的源代码:
internal static void ValidateEnd(Task task)
{
if (task.Status != TaskStatus.RanToCompletion)
HandleNonSuccess(task);
}
private static void HandleNonSuccess(Task task)
{
if (!task.IsCompleted)
{
try { task.Wait(); }
catch { }
}
if (task.Status != TaskStatus.RanToCompletion)
{
ThrowForNonSuccess(task);
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的结论:
可以看出GetResult()是调用TaskAwaiter.ValidateEnd(...),所以Task.Result不一样GetAwaiter.GetResult()。
我认为这 GetAwaiter().GetResult()是一个更好的选择,而不是.Result因为它不包含异常。
我在C# 7 in a Nutshell (Joseph Albahari & Ben Albahari) 书中的第 582 页读到了这篇文章
如果先行任务出错,则在继续代码调用 时重新抛出异常
awaiter.GetResult()。GetResult我们可以简单地访问前件的 Result 属性,而不是调用 。调用的好处GetResult是,如果前件出现故障,则直接抛出异常而不用包裹在 中AggregateException,从而允许更简单、更清晰的 catch 块。
来源:C# 7 in a Nutshell's page 582
| 归档时间: |
|
| 查看次数: |
91154 次 |
| 最近记录: |