Async, Await, and ConfigureAwait – Oh My!

By: Dan Lorenz | December 11, 2018

In .NET Framework 4.5, async/await keywords were added to the language to make async programming easier to work with. In order to maximize device resources and not block UI, you should really try to use asynchronous programming wherever you can. While async/await has made this much easier than before, there are a few gotchas that you may not know about. (See our app modernization capabilities)

New Keywords

Microsoft added async and await keywords into the .NET framework. In order to use them, however, the return type of the method should be of type Task. (There is one exception that we’ll discuss later.) In order to use the await keyword, you must have async in the method definition. If you put async in the method definition, you should have an await somewhere in the body, and you’ll normally get a warning about it from Visual Studio if you are missing one.

Here is an example of what it would look like in code:

public async Task ExecuteAsync(UpdateCarCommand request, CancellationToken token = default)

        {

            using (var context = _contextFactory.Create())

            {

                var entity = context.Cars.FirstOrDefault(a => a.Id == request.Id);

 

                // Mapping logic

 

                await context.SaveChangesAsync(token);

            }

        }

If you wanted to return something from an async method, you would use the generic form of Task. It would look something like this (if you wanted to return the number of affected rows):

public async Task<int> ExecuteAsync(UpdateCarCommand request, CancellationToken token = default)

        {

            using (var context = _contextFactory.Create())

            {

                var entity = context.Cars.FirstOrDefault(a => a.Id == request.Id);

 

                // Mapping logic

 

                return await context.SaveChangesAsync(token);

            }

        }

What Does It Buy Us?

While using this looks simple enough, how does it help? Ultimately, all this is doing is letting other requests use the current thread while we are waiting for a result back from the database (in this case). Any time you make a request to an external source like a database, disk, internet, etc. that could take a while to run, we can use async/await to let other requests use this thread. That way, we don’t have idle “workers” (threads) sitting around waiting for something else to get done. It would be like going to a fast food place where, after you order your food, no one else can order anything until you have your food. With async/await, other people can put in their order after you, and multiple orders can be worked on at the same time.

What It Doesn’t Do

One thing to be careful of here is that async/await is NOT parallel/multi-core programming. When you use async/await, you are only working with that single thread and letting something else use it. The code acts like it is “synchronous” because you can continue the code in the same method after an await. Thus, if you have four awaits in a single method, you would have to wait for each to finish before calling the next one. Thus, you’d have to spawn new threads with the Task Library, or whatever you like, to make them run in parallel. However, you could also make each of those threads use async/await so they aren’t blocking resources, too!

What Does .ConfigureAwait(false) Do?

By default, when you use async/await, it will resume on the original thread that started the request. However, if another long-running process currently has taken over that thread, you will be stuck waiting for it to complete. To avoid this issue, you can use a method called ConfigureAwait with a false parameter. When you do, this tells the Task that it can resume itself on any thread that is available instead of waiting for the thread that originally created it. This will speed up responses and avoid many deadlocks.

However, there is a small loss here. When you resume on another thread, the thread synchronization context is lost. The biggest loss here is that you would lose any Culture or Language settings, along with things like HttpContext.Current from the original thread, though this is no longer a problem in .NET Core. Thus, if you don’t need translations or access to any HttpContext type settings, you are safe to make this call. NOTE: If you need language/culture, you can always store the current values before an await and then re-apply it after the await on the new thread.

Here is an example of what ConfigureAwait(false) would look like:

public async Task<int> ExecuteAsync(UpdateCarCommand request, CancellationToken token = default)

        {

            using (var context = _contextFactory.Create())

            {

                var entity = context.Cars.FirstOrDefault(a => a.Id == request.Id);

 

                // Mapping logic

 

                return await context.SaveChangesAsync(token).CongifureAwait(false);

            }

        }

Caveats

Synchronous -> Asynchronous

There are a few things you need to keep track of if you are going to use async/await. The biggest issue you might run into has to deal with synchronous methods calling asynchronous methods. If you are starting a new project, you can normally put async/await through the entire chain of methods from top to bottom with little effort. However, if you are synchronous at the top and must call an async library, things can get a little dicey. You could end up with deadlocks all over the place if you aren’t careful.

If you have a synchronous method call an async method, you MUST use .ConfigureAwait(false). If you don’t, you will have an instant deadlock. What happens is that the main thread will call the async method, and you end up blocking this thread until that async method is complete. However, once that async method is complete, it must wait for the original caller to finish before it can resume. They are both waiting for each other to complete and never will. By using ConfigureAwait(false) on the call, the async method will be able to finish itself on another thread and alert the original thread that it is finished. The best way to make this call (so it throws the original exception directly) looks like this:

[HttpPut]

public IActionResult Put([FromBody]UpdateCommand command) =>

    _responseMediator.ExecuteAsync(command).ConfigureAwait(false).GetAwaiter().GetResult();

.NET Standard with ConfigureAwait(false)

In .NET Core, Microsoft did away with the SynchronizationContext that caused us to need ConfigureAwait(false) everywhere. Thus your ASP.NET Core app technically doesn’t need any ConfigureAwait(false) logic in them because it’s redundant. However, if you have a library that is using .NET Standard, then it is highly recommended that you still use .ConfigureAwait(false). In .NET Core, this will effectively do nothing. But if someone with .NET Framework ends up using this library and calls it synchronously, they will be in big trouble.

ConfigureAwait(false) All the Way Down

If there is a possibility that a synchronous call could call your asynchronous method, you end up being forced to put .ConfigureAwait(false) on every async call through the entire call stack! If you don’t, you’ll end up with another deadlock. The problem here is that each async/await is local to the current method that invoked it. Thus, each async/await through the chain could end up resuming on different threads. If a synchronous call goes all the way down and encounters a single Task without ConfigureAwait(false) on it, then this task is going to attempt to wait for that top, original thread to complete before it can resume. While this ends up tedious and annoying, it is critical that you don’t miss any of these calls.

Overhead

While async/await can greatly increase the number of requests your application can work on at once, there is a cost to using it. Each async/await call will end up creating a small state machine to keep track of everything. While small, this overhead can cause slowdowns if you abuse async/await. If you are doing strictly CPU computations, it is better to avoid using async/await since the CPU is not actually waiting for anything to return. Thus, it only makes it take longer to complete. Only when your thread could be actively waiting on something external to the application should you await it.

Async Void

While almost all your async/await methods should return a Task of some sort, there is one exception to this rule: sometimes, you can use async void. However, when you use this, the caller will not actually wait for that task to complete before resuming itself. It is effectively a fire-and-forget kind of thing. There are two cases where you’d want to use this. 

The first case is being an event handler, like a button click in WPF or WinForms. By default, an event handler’s definition must be void. If you put a Task on there, the program won’t compile, and an event returning something would feel weird. If that button is calling something async, then you must do async void for it to work properly. Luckily, this is what we want since this style won’t block the UI. 

The second one involves firing something off that you don’t mind waiting for to get the results. The most common example would be logging an information message, or something where you’d like to log something, but don’t want to wait for it to finish and don’t care if it completes or not. 

For both cases, however, there are a couple disadvantages. The first is that the calling method can’t try/catch any exceptions from the call. It will end up going to the AppDomain UnhandledException event. Though, if you put a try catch inside the actual async void method, you can effectively prevent this from happening. Another problem is that the caller will never know when it finishes since it doesn’t return anything. Thus, if you care when something completes, you really need to return a Task instead.

Conclusion

Hopefully you’ve learned more about the new, cool async/await feature and how to use it effectively! If you’re interested in seeing how we can help your organization, check out our custom software offerings.

Whether you are modernizing your application portfolio or deploying a collaboration platform, we put security at the forefront.

New call-to-action

Dan is a highly skilled Solution Architect with deep expertise in .NET languages, Windows Forms/Services, ASP.NET / JavaScript, MonoDroid, and Win 8 (XAML) - among others.

Subscribe to our Newsletter

Stay informed on the latest technology news and trends

Relevant Insights

24 New Updates to Microsoft Teams | March 2024

If you are a frequent reader of this blog series, you know that for the last few months I’ve been...
Read More about 24 New Updates to Microsoft Teams | March 2024

Overcoming Poor Help Desk Experience with the Right IT MSP

Reliable IT support services are essential to keep your digital infrastructure and operations efficient and secure. If you've hired an...
Read More about Overcoming Poor Help Desk Experience with the Right IT MSP