我最近阅读了一些使用了大量异步方法的代码,但有时需要同步执行它们。代码如下:
Foo foo = GetFooAsync(...).GetAwaiter().GetResult();
是一样的吗
Foo foo = GetFooAsync(...).Result;
我最近阅读了一些使用了大量异步方法的代码,但有时需要同步执行它们。代码如下:
Foo foo = GetFooAsync(...).GetAwaiter().GetResult();
是一样的吗
Foo foo = GetFooAsync(...).Result;
编辑:这是我13岁时写的,现在已经过时了。我推荐Nitin Agarwal的答案。
差不多。但有一个小区别:如果Task失败,GetResult()将直接抛出异常,而Task。结果将抛出AggregateException。然而,当它是异步的时候,使用它们有什么意义呢?更好的100倍选择是使用await。
另外,您不应该使用GetResult()。它仅供编译器使用,而不是供您使用。但是如果你不想要恼人的AggregateException,那就使用它。
https://github.com/aspnet/Security/issues/59
最后一点:你应该避免使用Task。结果和任务。等 尽可能多的,因为它们总是将内部异常封装在 并将该消息替换为通用消息(one或 出现了更多错误),这使得调试更加困难。即使 同步版本不应该经常使用,应该强烈使用 请考虑使用Task.GetAwaiter(). getresult()。
Task. getawaiter (). getresult()优先于Task。等待和任务。结果,因为它传播异常,而不是将异常包装在AggregateException中。但是,这三种方法都可能导致死锁和线程池不足问题。应该避免使用async/await。
下面的引用解释了为什么Task。等待和任务。结果不只是包含Task.GetAwaiter(). getresult()的异常传播行为(由于“非常高的兼容性栏”)。
As I mentioned previously, we have a very high compatibility bar, and thus we’ve avoided breaking changes. As such, Task.Wait retains its original behavior of always wrapping. However, you may find yourself in some advanced situations where you want behavior similar to the synchronous blocking employed by Task.Wait, but where you want the original exception propagated unwrapped rather than it being encased in an AggregateException. To achieve that, you can target the Task’s awaiter directly. When you write “await task;”, the compiler translates that into usage of the Task.GetAwaiter() method, which returns an instance that has a GetResult() method. When used on a faulted Task, GetResult() will propagate the original exception (this is how “await task;” gets its behavior). You can thus use “task.GetAwaiter().GetResult()” if you want to directly invoke this propagation logic.
https://devblogs.microsoft.com/pfxteam/task-exception-handling-in-net-4-5/
“GetResult”实际上意味着“检查任务中的错误” 一般来说,我尽量避免异步任务上的同步阻塞。然而,在一些情况下,我确实违反了这一原则。在这些罕见的情况下,我首选的方法是GetAwaiter(). getresult(),因为它保留任务异常,而不是将它们包装在AggregateException中。
https://blog.stephencleary.com/2014/12/a-tour-of-task-part-6-results.html
另一个区别是,当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
If a task faults, the exception is re-thrown when the continuation code calls awaiter.GetResult(). Rather than calling GetResult, we could simply access the Result property of the task. The benefit of calling GetResult is that if the task faults, the exception is thrown directly without being wrapped in AggregateException, allowing for simpler and cleaner catch blocks. For nongeneric tasks, GetResult() has a void return value. Its useful function is then solely to rethrow exceptions.
源码:c# 7.0简写
我查看了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页