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.
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 incompleteTask
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()
.
await Task vs Task.GetAwaiter
You have an async void
, your first code example is equivalent to
Task.Run(async () =>
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, new DispatchedHandler(FirstTask));
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, new DispatchedHandler(SecondTask));
});
private async void FirstTask()
{
await Task.Delay(2000);
textnew1.Text = "Dispatched";
}
private async void SecondTask()
{
await Task.Delay(2000);
textnew2.Text = "BackgroundTask";
}
Because the signature of DispatchedHandler is public delegate void DispatchedHandler()
you can't return a task with it. When you perform a async void
then RunAsync
thinks that the task is complete as soon as the await Task.Delay()
is hit.
async Task Wait behavior in static constructor waits for entire time even though task has finished
in a static constructor
Last time I checked, static constructors take a lock because only one of them can execute at a time. So, if the static constructor queues work to another thread that then does other things (i.e., call other static constructors) while the original static constructor is blocked on that other thread, then you can end up with a deadlock that is only resolved with the timeout.
What is best practice for this scenario?
Don't block on async
code, especially not in constructors, and especially especially not in static constructors. (Link is to my blog).
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.
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.
What option do I have instead of GetAwaiter.GetResult() in ASP.NET MVC 4 on .NET v4.6.1?
As mentioned in the comments, sync-over-async is always dangerous in .NET Framework on ASP.NET.
To avoid deadlocks, the following pattern (as described in Figure Six of Stephen Cleary's article allows you to split out your async request from your synchronous method call:
public SomeStuff GetSomestuff(Whatever whatever)
{
return GetSomestuffAsync(whatever).GetAwaiter().GetResult();
}
public async Task<SomeStuff> GetSomestuffAsync(Whatever whatever)
{
// Do NOT queue a synchronization context continuation.
return myRefitHttpClient.GetSomestuff(whatever).ConfigureAwait(false);
}
You can then verify via a unit test that your code does not deadlock with the AsyncContext
library from Stephen Cleary by simulating a synchronization context.
[TestMethod]
public void Verify_DoesNotDeadlock()
{
AsyncContext.Run(() =>
{
var whatever = new Whatever();
var response = service.GetSomestuff(whatever);
Assert.IsNotNull(response);
});
}
Related Topics
How to Check If Thread Finished Execution
How to Check If Another Instance of the Application Is Running
Webdriver How to Wait Until the Element Is Clickable in Webdriver C#
How to Get Text Between Nested Parentheses
A Pattern to Pause/Resume an Async Task
Change File Extension Using C#
What Happens While Waiting on a Task's Result
Datagridview Checkbox Column - Value and Functionality
Show Transparent Loading Spinner Above Other Controls
Understanding Foreignkey Attribute in Entity Framework Code First
C# Help Reading Foreign Characters Using Streamreader
How to Cast Expression<Func<T, Datetime>> to Expression<Func<T, Object>>
Foreach Control in Form, How to Do Something to All the Textboxes in My Form