Difference between calling .Wait() on an async method, and Task.Run().Wait() - c#

I have a server-side click event on a ASP.NET WebForms website. In this event I call a method which in turn calls its async partner method, adding .Wait() on the call.
This method then goes several levels down (i.e., calls another async method, which calls another async method, and so on) and eventually calls an async method on an HttpClient object. At this point the thread seems to disappear down a rabbit hole; the method never calls back.
Now, I know that the async series of calls works as expected because the same code is also called from a Web API controller (the controller method calls the async version of that first method, not the synchronous 'partner' method), which is fully async, and it returns as expected.
So basically I have something like this, which never returns
protected void btn_Click(object sender, EventArgs e)
> Class1.DoSomething()
> Class1.DoSomethingAsync.Wait()
...
> await ClassN.Authenticate()
{
await myHttpClient.PostAsync() // never returns
}
I did try using .ConfigureAwait(false) on that first async method but without any success.
I also have this, which does return
Task<IHttpActionResult> MyWebApiMethod()
> await Class1.DoSomethingAsync()
...
> await ClassN.Authenticate()
{
await myHttpClient.PostAsync() // does return
}
I've found that I can make the first version work if I change it to the following:
protected void btn_Click(object sender, EventArgs e)
> Class1.DoSomething()
> Task.Run(async () => await Class1.DoSomethingAsync()).Wait()
...
> await ClassN.Authenticate()
{
await myHttpClient.PostAsync()
}
But I don't know why.
Can anyone explain the difference between calling
Class1.DoSomethingAsync.Wait()
and calling
Task.Run(async () => await Class1.DoSomethingAsync()).Wait()

I explain this behavior in my blog post Don't Block on Asynchronous Code and my MSDN article on asynchronous best practices.
the thread seems to disappear down a rabbit hole; the method never calls back.
This happens because one of the awaits is attempting to resume on the ASP.NET context, but the request thread (using that ASP.NET context) is blocked waiting for the task to complete. This is what causes your deadlock.
I did try using .ConfigureAwait(false) on that first async method but without any success.
In order to avoid this deadlock using ConfigureAwait(false), it must be applied to every await in every method called. So DoSomethingAsync must use it for every await, every method that DoSomethingAsync calls must use it for every await (e.g., Authenticate), every method that those methods call must use it for every await (e.g., PostAsync), etc. Note that at the end here, you are dependent on library code, and in fact HttpClient has missed a few of these in the past.
I've found that I can make the first version work if I change it [to use Task.Run].
Yup. Task.Run will execute its delegate on a thread pool thread without any context. So this is why there's no deadlock: none of the awaits attempt to resume on the ASP.NET context.
Why don't you use it the correct way. async void btn_Click and await Class1.DoSomethingAsync() ?
Do not use Task.Run with already asynchronous methods, this is waste of threads. Just change signature of button_click eventhandler
And here's the right answer: don't block on asynchronous code. Just use an async void event handler and use await instead.
P.S. ASP.NET Core no longer has an ASP.NET context, so you can block as much as you want without fear of deadlocks. But you still shouldn't, of course, because it's inefficient.

Related

Is a method always async if it has the key word "async" and "await"? [duplicate]

I've been trying to understand async/await and Task in C# but have been failing spectacularly despite watching youtube videos, reading documentation and following a pluralsight course.
I was hoping someone might be able to help answer these slightly abstract questions to help my brain out.
1.Why do they say that async/await enables an 'asynchonrous' method when the async keyword on it's own does nothing and the await keyword adds a suspension point? Isn't adding a suspension point forcing the method to act synchronously, i.e. finish the task marked by the await before moving on.
2.Apparently you are not supposed to use async void except for event handlers, so how do you call an async method normally? It seems that in order to call an async method by using the await keyword, the method/class that is calling it itself needs to be marked as async. All the examples I've seen have 'initiated' an async void method with an event handler. How would you 'escape' this wrapping of async/await to run the method?
3.
public async Task SaveScreenshot(string filename, IWebDriver driver)
{
var screenshot = driver.TakeScreenshot();
await Task.Run(() =>
{
Thread.Sleep(2000);
screenshot.SaveAsFile(filename, ScreenshotImageFormat.Bmp);
Console.WriteLine("Screenshot saved");
});
Console.WriteLine("End of method");
}
Relating back to 1. this looks like a synchronous method. Execution pauses when it gets to Task.Run, therefore Console.WriteLine("End of method"); will not be executed until the task is finished. Maybe the whole method itself will be executed asynchronously at the point it is triggered in the code? But relating back to 2, you need to call this with an await otherwise you get the message 'Because this call is not awaited..' therefore adding an await will cause that execution point to be synchronous and so on and so.
Any help understanding this would be much appreciated.
Isn't adding a suspension point forcing the method to act synchronously, i.e. finish the task marked by the await before moving on.
No, the word you're thinking of is "sequential", not "synchronous". await results in asynchronous sequential code. "Sequential" meaning "one at a time"; "synchronous" meaning "blocking until completed".
how do you call an async method normally?
Using await.
How would you 'escape' this wrapping of async/await to run the method?
Ideally, you don't. You go async all the way. Modern frameworks (including ASP.NET MVC, Azure Functions / WebJobs, NUnit / xUnit / MSTest, etc) all allow you to have entry points that return Task. Less-modern frameworks (including WinForms, WPF, Xamarin Forms, ASP.NET WebForms, etc) all allow async void entry points.
So, ideally you do not call asynchronous code from synchronous code. This makes sense if you think about what asynchronous code is: the entire point of it is to not block the calling thread, so if you block the calling thread on asynchronous code, then you lose all the benefits of asynchronous code in the first place.
That said, there are rare situations where you do need to treat the code synchronously. E.g., if you are in the middle of a transition to async, or if you are constrained by a library/framework that is forcing your code to be synchronous and won't work with async void. In that case, you can employ one of the hacks in my article on brownfield async.
Your understanding is pretty good :). The main point you seem to be missing is that "asynchronous" methods in .NET mean methods that can stop execution without blocking the calling thread.
As you pointed out in (1), the async keyword basically enables the use of await and requires the return type to be void or Task/Task<T>. await just instructs the current method to suspend execution until the task is complete.
What you are missing here is that it suspends just the current method. It does not block the thread the method was executing on. This is important in cases like the UI thread of a WPF application. Suspend method execution and everything keeps running, block the thread and the application stops responding.
You usually want your async calls to go all the way to the top (like an event handler), this allows the most flexibility and prevents deadlock situations. However; you can wait for a Task returning method to complete with Wait:
someAsyncMethod.Wait()
Or get the return value:
var result = someAsyncMethod.Result;
Note that both of these are synchronous and block the calling thread. Doing this can cause deadlock if the async task is waiting for some other work on the calling thread to complete.
The above should answer your question in (3); the method itself appears to execute synchronously (this is the magic of await/async) but the task doesn't block the calling thread.
It is asynchronous because you don't have to wait the method to return. In your code, you may call the async method and save the task in a variable. Continue doing something else. Later, when the method result is needed, you await the response (task).
// Synchronous method.
static void Main(string[] args)
{
// Call async methods, but don't await them until needed.
Task<string> task1 = DoAsync();
Task<string> task2 = DoAsync();
Task<string> task3 = DoAsync();
// Do other stuff.
// Now, it is time to await the async methods to finish.
Task.WaitAll(task1, task2, task3);
// Do something with the results.
Console.WriteLine(task1.Result);
Console.ReadKey();
}
private static async Task<string> DoAsync()
{
Console.WriteLine("Started");
await Task.Delay(3000);
Console.WriteLine("Finished");
return "Success";
}
// Output:
// Started
// Started
// Started
// Finished
// Finished
// Finished
// Success

WPF await switches from the UI thread

I have a function called from the UI thread, I'm double checking if that's so by
System.Windows.Threading.Dispatcher.FromThread(System.Threading.Thread.CurrentThread)
everything is called from a void method, which can't be async void because it has an out parameter (method from 3rd party API).
So in this void method I make a call to an async method, the main method doesn't have to wait, so I just run it and forget (planning to handle exceptions in the ContinueWith method, but I'm not there yet).
Problem is, that when in the method chain an await is performed on the ExecuteTaskAsync method of the RestSharp client, after that line, the thread is witched to MTA, the SynchronizationContext.Current is different too.
Why is this happening? Shouldn't the await keyword return to the orginal SynchronizationContext? Why is the SynchronizationContext.Current changing during that call?
Sorry for no code snippets, it's a part of a bigger app and it's hard to snippet it out, but I'm doing it right now, so code snippets will be added.
Ok, resolved it, the mentioned void method from the 3rd party API, was running with a thread that has a dispatcher, because
System.Windows.Threading.Dispatcher.FromThread(System.Threading.Thread.CurrentThread)
Returned a dispatcher, I don't know why, but that didn't matter, I had to run my async method in an Invoke like
Application.Current.Dispatcher.Invoke(() => myAsyncMethod());
and then the await in my method didn't switched the SynchronizationContext.
It looks like my assumption that when there is a dispatcher it's the proper context was wrong, don't know why :)

Call async function inside non-async function

Before I use
protected async void OnResume() {
await DoWorkAsync(); // assume exception is appropriately handled
await DoOtherWorkAsync();
}
But now I saw other developers use
protected void OnResume() {
SynchronizationContext.Current.Post(async (status) => {
await DoWorkAsync();
await DoOtherWorkAsync();}, null);
}
Is the second one correct and preferred when calling async methods inside non-async methods? Thank you in advance.
UPDATE:
If you use the 2nd snippet, you must call SynchronizationContext.Current.Post() on UI Thread, or you will get NullReferenceException, cause SynchronizationContext.Current is null on non-UI Thread. _handler.Post(async() => {// do UI changes}); can be called on non-UI Thread to do any UI changes, as long as _handler is instantiated on UI Thread.
No, don't use SynchronizationContext. You're already on the UI thread, so the second option really makes no sense.
Note that normally async void should be avoided. In this case, I'd say it's acceptable because you're actually treating OnResume as though it were an event handler:
protected async void OnResume()
{
await DoWorkAsync();
}
Both examples are performing unnecessary work. The best implementation of the method would be:
protected void OnResume()
{
DoWorkAsync();
}
In the first case you're forcing a continuation to run in the current context but that continuation has nothing to actually do; it's just pointlessly adding a post to the synchronization context.
The second snippet is adding two pointless posts two the current synchronization context, one explicitly, and one implicitly.
Both methods also have async methods that are adding state machines, even though you don't actually leverage any of that functionality, so that overhead is all entirely wasted.

what are the circumstances of using .Wait() when calling async methods

I have the following async long running method inside my asp.net mvc-5 web application :-
public async Task<ScanResult> ScanAsync(string FQDN)
{
// sample of the operation i am doing
var c = await context.SingleOrDefaultAsync(a=>a.id == 1);
var list = await context.Employees.ToListAsync();
await context.SaveChangesAsync();
//etc..
}
and i am using Hangfire tool which support running background jobs to call this async method on timely basis, but un-fortuntly the hangefire tool does not support calling async methods directly . so to overcome this problem i created a sync version of the above method , as follow:-
public void Scan()
{
ScanAsync("test").Wait();
}
then from the HangFire scheduler i am calling the sync method as follow:-
RecurringJob.AddOrUpdate(() => ss.Scan(), Cron.Minutely);
so i know that using .Wait() will mainly occupy the iis thread during the method execution ,, but as i mentioned i need to do it in this way as i can not directly call an async TASK inside the hangefire scheduler .
so what will happen when i use .Wait() to call the async method ?, will the whole method's operations be done in a sync way ? for example as shown above i have three async operations inside the ScanAsync() ;SingleOrDefualtAsync,ToListAsync & SaveChangesAsync, so will they be executed in sync way because i am calling the ScanAsync method using .wait() ?
so what will happen when i use .Wait() to call the async method ?,
will the whole method's operations be done in a sync way ? for example
as shown above i have three async operations inside the ScanAsync()
;SingleOrDefualtAsync,ToListAsync & SaveChangesAsync, so will they be
executed in sync way because i am calling the ScanAsync method using
.wait() ?
The methods querying the database will still be executed asynchronously, but the fact that you're calling Wait means that even though you're releasing the thread, it wont return to the ASP.NET ThreadPool as you're halting it.
This is also a potential for deadlocks, as ASP.NET has a custom synchronization context which makes sure the context of the request is availiable when in a continuation of an async call.
I would recommend that instead, you'd use the synchronous API provided by entity-framework, as you won't actually be enjoying the scalability one can get from asynchronous calls.
Edit:
In the comments, you asked:
As i am currently doing with hangefire eliminate the async effect ? if yes then will it be better to use sync methods instead ? or using sync or async with hangeffire will be exactly the same
First, you have to understand what the benefits of async are. You don't do it because it's cool, you do it because it serves a purpose. What is that purpose? Being able to scale under load. How does it do that? When you await an async method, control is yielded back to the caller. For example, you have an incoming request, you query you database. You can either sit there and wait for the query to finish, or you can re-use that thread to serve more incomong requests. That's the true power.
If you don't actually plan on receiving a decent amount of requests (such that you'll starve the thread-pool), you won't actually see any benefit from async. Currently, the way you've implemented it, you won't see any of those benefits because you're blocking the async calls. All you'll see, possibly, are deadlocks.
This very much depends on the way HangFire is implemented. If it just queuing tasks to be invoked in ThreadPool the only effect will be, that one of your threads will be blocked until the request is ended. However if there is a custom SynchronizationContext this can lead to a serious deadlock.
Consider, if you really want to wait for your scheduled job to be done. Maybe all you want is just a fire and forget pattern. This way your method will be like:
public void Scan()
{
ScanAsync("test"); // smoothly ignore the task
}
If you do need to wait, you can instead try using async void method:
public async void Scan()
{
await ScanAsync("test");
DoSomeOtherJob();
}
There are many controversies about using async void as you cannot wait for this method to end, nor you can handle possible errors.
However, in event driven application this can be the only way. For more informations you can refer to: Async Void, ASP.Net, and Count of Outstanding Operations

SignalR invoke not returning when used in an async method

I'm not sure if this is a SignalR issue or an async/await issue. When my client app (WPF) starts up, it does some initialisation:-
public async void Initialise()
{
// Get data from the server - async, as it may be long-running.
var data = await _hubProxy.Invoke<FooData>("Method1");
_dataProcessor.ProcessData(data);
}
The _dataProcessor is a helper class that does some stuff with the data passed to it, then at some point calls a different server method using a line similar to:-
var moreData = _hubProxy.Invoke<BarData>("Method2").Result;
None of the code in the helper class is async.
The first server invoke (in Initialise()) works fine - it gets back the data and passes it to the helper class. This proceeds without issue until its invoke - it gets called but never returns, and the thread never proceeds past this line. I've put a breakpoint in the server method and can confirm that it is being called and returning a value, but for some reason this is not getting back to the client.
The strange thing is, if I take out the async/await keywords in the Initialise() method, everything works fine. What am I doing wrong? Why would these keywords affect the synchronous code in the helper class?
(I realise you shouldn't normally use async void, but as the Initialise() method is "fire and forget", I thought it was okay in this scenario).
This line causes a deadlock on the UI thread:
var moreData = _hubProxy.Invoke<BarData>("Method2").Result;
Make ProcessData an async method and await _hubProxy.Invoke inside it:
var moreData = await _hubProxy.Invoke<BarData>("Method2");
Then, await _dataProcessor.ProcessData in your Initialise:
await _dataProcessor.ProcessData(data);
Make sure you don't use .Result or .Wait anywhere else.
Another way of solving it is just this:
public async void Initialise()
{
// Get data from the server - async, as it may be long-running.
var data = await _hubProxy.Invoke<FooData>("Method1").ConfigureAwait(false);
_dataProcessor.ProcessData(data);
}
Note ConfigureAwait(false). In this case the continuation after await will happen on a non-UI thread. This will eliminate the deadlock, but this is not an ideal solution, rather a workaround. Moreover, your logic may require the continuation on the UI thread, for UI access or some thread-safety concerns.
The best solution would be to use both ConfigureAwait(false) (if possible) and avoid blocking with .Result or .Wait, at the same time.

Categories