我最近阅读了一些使用了大量异步方法的代码,但有时需要同步执行它们。代码如下:
Foo foo = GetFooAsync(...).GetAwaiter().GetResult();
是一样的吗
Foo foo = GetFooAsync(...).Result;
我最近阅读了一些使用了大量异步方法的代码,但有时需要同步执行它们。代码如下:
Foo foo = GetFooAsync(...).GetAwaiter().GetResult();
是一样的吗
Foo foo = GetFooAsync(...).Result;
当前回答
如前所述,如果你可以使用await。如果你需要像你提到的. getawaiter (). getresult()那样同步运行代码,. result或. wait()有死锁的风险,正如许多人在评论/回答中所说的那样。因为我们大多数人都喜欢一行程序,所以你可以在。net 4.5中使用它们。
通过async方法获取一个值:
var result = Task.Run(() => asyncGetValue()).Result;
同步调用异步方法
Task.Run(() => asyncMethod()).Wait();
由于使用Task.Run,不会出现死锁问题。
来源:
https://stackoverflow.com/a/32429753/3850405
更新:
如果调用线程来自线程池,则可能导致死锁。发生如下情况:一个新任务排队到队列的末尾,最终将执行该任务的线程池线程被阻塞,直到该任务执行为止。
来源:
https://medium.com/rubrikkgroup/understanding-async-avoiding-deadlocks-e41f8f2c6f5d
其他回答
我查看了TaskOfResult.cs的源代码(TaskOfResult.cs的源代码):
如果任务未完成,则Task。结果将调用getter中的Task.Wait()方法。
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);
}
}
}
如果我们调用Task的GetAwaiter方法,Task将包装TaskAwaiter<TResult> (GetAwaiter的源代码()),(TaskAwaiter的源代码):
public TaskAwaiter GetAwaiter()
{
return new TaskAwaiter(this);
}
如果我们调用TaskAwaiter<TResult>的GetResult()方法,它将调用Task。结果属性,即Task。结果将调用任务的Wait()方法(GetResult()的源代码):
public TResult GetResult()
{
TaskAwaiter.ValidateEnd(m_task);
return m_task.Result;
}
它是ValidateEnd(任务任务)的源代码(ValidateEnd(任务任务)的源代码):
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);
}
}
这是我的结论:
可以看到GetResult()正在调用TaskAwaiter.ValidateEnd(…),因此Task。结果不是相同的GetAwaiter.GetResult()。
我认为GetAwaiter(). getresult()是一个更好的选择,而不是. result,因为后者不包装异常。
我在《c# 7 in a Nutshell》(Joseph Albahari & Ben Albahari)的第582页读到了这一点。
如果一个先行任务出现错误,异常将在 延续代码调用awaiter.GetResult()。而不是打电话 的Result属性,我们可以简单地访问 先行词。调用GetResult的好处是如果 如果是前因错误,则直接抛出异常 封装在AggregateException中,允许更简单和更干净 catch块。
来源:c# 7 in a Nutshell的第582页
编辑:这是我13岁时写的,现在已经过时了。我推荐Nitin Agarwal的答案。
差不多。但有一个小区别:如果Task失败,GetResult()将直接抛出异常,而Task。结果将抛出AggregateException。然而,当它是异步的时候,使用它们有什么意义呢?更好的100倍选择是使用await。
另外,您不应该使用GetResult()。它仅供编译器使用,而不是供您使用。但是如果你不想要恼人的AggregateException,那就使用它。
另一个区别是,当async函数只返回Task而不是Task<T>时,您不能使用
GetFooAsync(...).Result;
而
GetFooAsync(...).GetAwaiter().GetResult();
仍能工作。
我知道问题中的示例代码是针对Task<T>的情况,但是这个问题是一般的。
如前所述,如果你可以使用await。如果你需要像你提到的. getawaiter (). getresult()那样同步运行代码,. result或. wait()有死锁的风险,正如许多人在评论/回答中所说的那样。因为我们大多数人都喜欢一行程序,所以你可以在。net 4.5中使用它们。
通过async方法获取一个值:
var result = Task.Run(() => asyncGetValue()).Result;
同步调用异步方法
Task.Run(() => asyncMethod()).Wait();
由于使用Task.Run,不会出现死锁问题。
来源:
https://stackoverflow.com/a/32429753/3850405
更新:
如果调用线程来自线程池,则可能导致死锁。发生如下情况:一个新任务排队到队列的末尾,最终将执行该任务的线程池线程被阻塞,直到该任务执行为止。
来源:
https://medium.com/rubrikkgroup/understanding-async-avoiding-deadlocks-e41f8f2c6f5d
https://github.com/aspnet/Security/issues/59
最后一点:你应该避免使用Task。结果和任务。等 尽可能多的,因为它们总是将内部异常封装在 并将该消息替换为通用消息(one或 出现了更多错误),这使得调试更加困难。即使 同步版本不应该经常使用,应该强烈使用 请考虑使用Task.GetAwaiter(). getresult()。