Task.Result Follow Task.GetAwaiter.GetResult Is () the same? How to choose?

Time:2020-11-27

A few days ago, when using thread pool to perform some tasks, it was transported to a situation where asynchronous methods were used in callback methods, but the callback methods did not seem to support themasync awaitHow to write it. What should I do at this time? It’s usingTask.ResultTo get the returned result, or to use theGetAwaiter.GetResult()What about it? This article will discuss it.

Author: Yi lezhu

Original address: https://www.cnblogs.com/yilezhu/p/13168337.html

First of all, the pseudo code of this scenario is given

ThreadPool.QueueUserWorkItem(ExcuteScanProcess, node);

stayExcuteScanProcessIn this callback method

private void ExcuteScanProcess(object state)
{
    …… Other processing
    repository.UpdateAsync(node).ConfigureAwait(false).GetAwaiter().GetResult();
    …… Other processing
}

As shown in the figure aboverepository.UpdateAsync(node)It belongs to a method. At this time, I want to wait for its asynchronous execution to complete before executing the subsequent logic. At this time, I have two choices: direct

repository.UpdateAsync(node).ConfigureAwait(false).GetAwaiter().GetResult();

OK, or

repository.UpdateAsync(node).ConfigureAwait(false).Result;

Okay?

For this reason, I searched for relevant information and made a simple summary of the differences between them

In fact, these two ways of use are similar. However, there is a small difference: if the task fails,Task.GetAwaiter().GetResult()The exception is thrown directly, andTask.ResultThe exception will be packaged in theAggregateExceptionMedium. From this point of viewTask.GetAwaiter().GetResult()Better thanTask.Result。 After all, it lacks the exception wrapping operation, that is, it throws the exception directly instead of wrapping the exception in theAggregateExceptionMedium.

The following introduction explains whyTask.ResultIt’s not just aboutTask.GetAwaiter().GetResult()Exception propagation behavior (due to “very high compatibility”).

As mentioned earlier, we have a very high compatibility standard, so we avoid changes. So,Task.WaitIt retains the original behavior of always wrapping. However, you may find yourself in advanced situations where you want to behave like the synchronous blocking you useTask.Wait, but you want to expand the original exception rather than propagate it, rather than encapsulate it in theAggregateExceptionMedium. To do this, you can directly locate the waiting party for the task. When you write“await task;”When the compilerTask.GetAwaiter()Will be converted to the usage of the method, which returns aGetResult()Method. When used for a failed task,GetResult()The original exception will be propagated (this is“await task;”How to obtain their behavior). Therefore, you can use the“task.GetAwaiter().GetResult()If you want to call this propagation logic directly.

https://blogs.msdn.microsoft.com/pfxteam/2011/09/28/task-exception-handling-in-net-4-5/

GetResult”It actually means “check for errors in the task”

I usually try to avoid blocking tasks asynchronously. However, in a few cases, I did violate the code. In those rare cases, my preferred approach isGetAwaiter().GetResult()Because it retains task exceptions rather than wrapping them inAggregateException

summary

So in those scenarios where asynchronous tasks must be blocked synchronously, I choose to use theGetAwaiter().GetResult()