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.
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).
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 ofawait
and requires the return type to bevoid
orTask/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 aTask
returning method to complete withWait
:Or get the return value:
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.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".Using
await
.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 allowasync 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.