Reputation: 46496
I was recently reading some code that uses a lot of async methods, but then sometimes needs to execute them synchronously. The code does:
Foo foo = GetFooAsync(...).GetAwaiter().GetResult();
Is this the same as
Foo foo = GetFooAsync(...).Result;
Upvotes: 582
Views: 289929
Reputation: 610
Task.Result
, .GetAwaiter().GetResult()
, and Task.Wait
are all mechanisms to wait synchronously for a Task
to complete in C#, but they differ in their behavior and side effects:
Task.Result
:
Task.Result
will
rethrow the original exception wrapped in an AggregateException
.AggregateException
and then check its InnerExceptions
..GetAwaiter().GetResult()
:
Task.Result
, it rethrows the original exception directly,AggregateException
.Task.Wait
:
Task.Result
.Task.Result
, it wraps any exceptions in an
AggregateException
.In summary, Task.Result
and Task.Wait
are similar in that they both wrap exceptions in an AggregateException
, but Task.Result
is used when you need the result of the task. .GetAwaiter().GetResult()
is preferred when you need both the result of the task and direct exception handling. All three should be used cautiously to avoid deadlocks, especially in UI applications or in asynchronous code paths.
Upvotes: 8
Reputation: 5442
I checked the source code of TaskOfResult.cs
(Source code of TaskOfResult.cs):
If Task
is not completed, Task.Result
will call Task.Wait()
method in 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);
}
}
}
If we call the GetAwaiter
method of Task
, Task
will wrapped TaskAwaiter<TResult>
(Source code of GetAwaiter()), (Source code of TaskAwaiter) :
public TaskAwaiter GetAwaiter()
{
return new TaskAwaiter(this);
}
And if we call the GetResult()
method of TaskAwaiter<TResult>
, it will call Task.Result
property, that Task.Result
will call Wait()
method of Task
( Source code of GetResult()):
public TResult GetResult()
{
TaskAwaiter.ValidateEnd(m_task);
return m_task.Result;
}
It is source code of ValidateEnd(Task task)
( Source code of 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);
}
}
This is my conclusion:
As can be seen GetResult()
is calling TaskAwaiter.ValidateEnd(...)
, therefore Task.Result
is not same GetAwaiter.GetResult()
.
I think GetAwaiter().GetResult()
is a better choice instead of .Result
because the former doesn't wrap exceptions.
I read this at page 582 in C# 7 in a Nutshell (Joseph Albahari & Ben Albahari).
If an antecedent task faults, the exception is re-thrown when the continuation code calls
awaiter.GetResult()
. Rather than callingGetResult
, we could simply access the Result property of the antecedent. The benefit of callingGetResult
is that if the antecedent faults, the exception is thrown directly without being wrapped inAggregateException
, allowing for simpler and cleaner catch blocks.
Source: C# 7 in a Nutshell's page 582
Upvotes: 31
Reputation: 4270
Task.GetAwaiter().GetResult()
is preferred over Task.Wait
and Task.Result
because it propagates exceptions rather than wrapping them in an AggregateException
. However, all three methods cause the potential for deadlock and thread pool starvation issues. They should all be avoided in favor of async/await
.
The quote below explains why Task.Wait
and Task.Result
don't simply contain the exception propagation behavior of Task.GetAwaiter().GetResult()
(due to a "very high compatibility bar").
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 byTask.Wait
, but where you want the original exception propagated unwrapped rather than it being encased in anAggregateException
. To achieve that, you can target the Task’s awaiter directly. When you write “await task;
”, the compiler translates that into usage of theTask.GetAwaiter()
method, which returns an instance that has aGetResult()
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
” actually means “check the task for errors”In general, I try my best to avoid synchronously blocking on an asynchronous task. However, there are a handful of situations where I do violate that guideline. In those rare conditions, my preferred method is
GetAwaiter().GetResult()
because it preserves the task exceptions instead of wrapping them in anAggregateException
.
https://blog.stephencleary.com/2014/12/a-tour-of-task-part-6-results.html
Upvotes: 338
Reputation: 22794
EDIT: This was written when I was 13, and is out of date. I recommend Nitin Agarwal's answer instead.
Pretty much. One small difference though: if the Task
fails, GetResult()
will just throw the exception caused directly, while Task.Result
will throw an AggregateException
. However, what's the point of using either of those when it's async
? The 100x better option is to use await
.
Also, you're not meant to use GetResult()
. It's meant to be for compiler use only, not for you. But if you don't want the annoying AggregateException
, use it.
Upvotes: 254
Reputation: 69958
As already mentioned if you can use await
. If you need to run the code synchronously like you mention .GetAwaiter().GetResult()
, .Result
or .Wait()
is a risk for deadlocks as many have said in comments/answers. Since most of us like oneliners you can use these for .Net 4.5<
Acquiring a value via an async method:
var result = Task.Run(() => asyncGetValue()).Result;
Syncronously calling an async method
Task.Run(() => asyncMethod()).Wait();
No deadlock issues will occur due to the use of Task.Run
.
Source:
https://stackoverflow.com/a/32429753/3850405
Update:
Could cause a deadlock if the calling thread is from the threadpool. The following happens: A new task is queued to the end of the queue, and the threadpool thread which would eventually execute the Task is blocked until the Task is executed.
Source:
https://medium.com/rubrikkgroup/understanding-async-avoiding-deadlocks-e41f8f2c6f5d
Upvotes: 34
Reputation: 153
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.
source : c# 7.0 in a Nutshell
Upvotes: 2
Reputation: 9842
Another difference is when async
function returns just Task
instead of Task<T>
then you cannot use
GetFooAsync(...).Result;
Whereas
GetFooAsync(...).GetAwaiter().GetResult();
still works.
I know the example code in the question is for the case Task<T>
, however the question is asked generally.
Upvotes: 48
Reputation: 931
https://github.com/aspnet/Security/issues/59
"One last remark: you should avoid using
Task.Result
andTask.Wait
as much as possible as they always encapsulate the inner exception in anAggregateException
and replace the message by a generic one (One or more errors occurred), which makes debugging harder. Even if the synchronous version shouldn't be used that often, you should strongly consider usingTask.GetAwaiter().GetResult()
instead."
Upvotes: 79