Is Task.Result the Same as .Getawaiter.Getresult()

When to use Task.Run().GetAwaiter().GetResult() and ().GetAwaiter.GetResult()?

When you use Task.Run, the initial synchronous part of your delegate is run on a threadpool thread, whereas just ().GetAwaiter().GetResult() will run that synchronous part on the same thread.

Using Task.Run(...).GetAwaiter().GetResult() can be used as a workaround to run async code and wait on it synchronously, it will not result in an async deadlock, whereas ().GetAwaiter().GetResult() could. Do be aware that it still isn't "safe", in that you likely are blocking within a threadpool thread, on servers this can lead to thread pool exhaustion at load.

If you want to run a Task returning method, and know that the initial synchronous part is trivial, and you know that the rest of the async method will not run with a SynchronizationContext, just ().GetAwaiter().GetResult() can be a micro-optimization, I'd say only do it if you know exactly what you are doing.

How do you know that you are running under no SynchronizationContext? SynchronizationContext.Current will be null, due to one the following reasons:

  • You know your code is running under an application model that doesn't have one (Console app, ASP.NET Core, Windows Service)
  • You have used .ConfigureAwait(false) on an awaited incomplete Task previously in the current stack.
  • You have explicitly called SynchronizationContext.SetSynchronizationContext(null)

So you see, it's a lot to think about, so in general you almost always want to use Task.Run(...).GetAwaiter.GetResult().

What's the difference between AsyncFunction().GetAwaiter().GetResult() and Task.Run(() = AsyncFunction).GetAwaiter().GetResult()?

await is aware of a thing called SynchronizationContext. If such context is present (SynchronizationContext.Current is not null) then continuation after await is passed to that context, because it should know better how to handle it (unless you explicitly tell to not do that, using await someTask.ConfigureAwait(continueOnCapturedContext: false))

Most UI frameworks do not like when UI is accessed from multiple threads concurrently, because it often leads to various hard to debug problems. They utilize SynchronizationContext to enforce single flow of execution. To do that - they usually put callbacks posted to SynchronizationContext in some sort of queue, and execute them one by one on the single "UI" thread.

Blazor also does that. GetAwaiterGetResultOnFunction is your case is executed on that "UI" thread, with SynchronizationContext available. When execution reaches await Task.Delay inside AsyncFunction - current context is captured and it's noted that when Task.Delay completes - the rest of the function should be posted to the context for execution.

Then you block the "UI" thread by doing GetResult on a task returned from AsyncFunction. Then Task.Delay completes and the rest of the function is posted to the SynchronizationContext of Blazor to execute. However, to do that Blazor needs the same UI thread you just blocked, resulting in deadlock.

When you call GetAwaiterGetResultOnTask - AsyncFunction does not run on "UI" thread of Blazor - it runs on some other (thread pool) thread. SynchronizationContext is null, the part after await Task.Delay will run on some thread pool thread and will not require "UI" thread to complete. Then there is no deadlock.

Is .GetAwaiter().GetResult(); safe for general use?

As I describe on my blog, GetAwaiter().GetResult() can deadlock when it's used in a one-thread-at-a-time context. This is most commonly seen when called on the UI thread or in an ASP.NET context (for pre-Core ASP.NET).

Wait has the same problems. The appropriate fix is to use await, and make the calling code asynchronous.

Note that the Main method in Console apps is an exception to this rule; it is perfectly appropriate to use there. Many code samples use it in this way.

Still confused on ConfigureAwait(false) used with GetAwaiter and GetResult in C#. Getting a deadlock or method not returning


I thought (especially from the first article above) that if I use ConfigureAwait(false) I could call an async method without a deadlock.

There's an important note in that article:

Using ConfigureAwait(false) to avoid deadlocks is a dangerous practice. You would have to use ConfigureAwait(false) for every await in the transitive closure of all methods called by the blocking code, including all third- and second-party code. Using ConfigureAwait(false) to avoid deadlock is at best just a hack).

So, is ConfigureAwait(false) used for every await in the transitive closure? This means:

  • Does CloseLoadDoor use ConfigureAwait(false) for every await? We can see from the code posted that it does.
  • Does _model.CloseLoadDoor use ConfigureAwait(false) for every await? That we cannot see.
  • Does every method called by _model.CloseLoadDoor use ConfigureAwait(false) for every await?
  • Does every method called by every method called by _model.CloseLoadDoor use ConfigureAwait(false) for every await?
  • etc.

This is a severe maintenance burden at least. I suspect that somewhere down the call stack, there's a missing ConfigureAwait(false).

As that note concludes:

As the title of this post points out, the better solution is “Don’t block on async code”.

In other words, the whole point of that article is "Don't Block on Async Code". It's not saying "Block on Async Code with This One Neat Trick".

If you do want to have an API that supports both synchronous and asynchronous callers, I recommend using the bool argument hack in my article on brownfield async.


On a side note, in the code CloseLoadDoor().ConfigureAwait(false).GetAwaiter().GetResult(), the ConfigureAwait doesn't do anything. It's "configure await", not "configure task". Since there's no await there, the ConfigureAwait(false) has no effect.

What is the difference between .Wait() vs .GetAwaiter().GetResult()?

Both are a synchronous wait for the result of the operation (and you should avoid those if possible).

The difference is mainly in handling exceptions. With Wait, the exception stack trace is unaltered and represents the actual stack at the time of the exception, so if you have a piece of code that runs on a thread-pool thread, you'd have a stack like

ThreadPoolThread.RunTask
YourCode.SomeWork

On the other hand, .GetAwaiter().GetResult() will rework the stack trace to take all the asynchronous context into account, ignoring that some parts of the code execute on the UI thread, and some on a ThreadPool thread, and some are simply asynchronous I/O. So your stack trace will reflect a synchronous-like step through your code:

TheSyncMethodThatWaitsForTheAsyncMethod
YourCode.SomeAsyncMethod
SomeAsync
YourCode.SomeWork

This tends to make exception stack traces a lot more useful, to say the least. You can see where YourCode.SomeWork was called in the context of your application, rather than "the physical way it was run".

An example of how this works is in the reference source (non-contractual, of course).

Does GetAwaiter() throw a single exception?

Yeah, you can and should use async await :

(var success, var response) = await HTTPHelper.SendRequest<SomeJsonResponse>( ... )

Then there is no need to wrap it into the Task. I assume .SendRequest<SomeJsonResponse>() returns the Task.

FYI: .Wait(), .Result or GetAwaiter().GetResult() are all thread blocking executions

Neither GetAwaiter().GetResult() nor .Result worked for me but Task.Run(await ()= nameOfMethodTobeCalled()).Result working. I didn't understand?


So for that I called those method and to retrieve result I used

A better solution is to use await and allow the asynchrony to grow naturally through the code base. If you do have to do sync-over-async, then direct blocking like that may cause deadlocks.

await captures a context by default, and in your case the await inside of MyMethod is capturing an ASP.NET request context, which existed on pre-Core versions of ASP.NET. Then, the calling code blocks a thread in that request context by calling GetResult()/Result. Later, when the await is ready to continue, it schedules the remainder of MyMethod to that context. But it will never be run because there's a thread blocked in that context waiting for MyMethod to complete.

The reason Task.Run doesn't deadlock is because MyMethod uses the thread pool context instead of an ASP.NET request context. This is the "thread pool hack" approach to doing sync-over-async. However, Task.Run is not recommended on ASP.NET; if possible, change the code to use await instead.



Related Topics



Leave a reply



Submit