What does ConfigureAwait(false) actually do?

Veljko Zrnić
7 min readNov 12, 2020

In the land of asynchronous programming there’s always room for mystery. Let’s try to unravel this one, as I haven’t found a clear explanation of the behavior which I (now) find obvious.

You’ve probably seen something like this:

var result = await reader.ReadAsync<Dto>().ConfigureAwait(false);

If you search online, you will find many technical details, but let’s explain this using common things we know & love — dining out!

Photo by Kate Townsend on Unsplash

Imagine our web service is a running restaurant — the restaurant employs waiters (threads) that go and serve the customers (requests), giving them their order from the kitchen (some database query).

What’s going on is this:

  1. Customer (request) comes in
  2. Waiter (thread) comes and serves the customer by taking it’s order.
  3. Waiter goes to the kitchen and delivers the order (issues the db command)
  4. Kitchen prepares the meal, and lets the waiter know it’s ready (‘select’ statement completed, and results are read)
  5. Waiter takes the meal (results) and delivers it to the customer.

This is a fairly typical async scenario — the waiter doesn’t wait by the kitchen for the meal to be ready (what would be called a synchronous action), but rather passes on the order to them and is free to do other stuff, serve other customers. Once the meal is prepared, the kitchen (say, a db library) lets him know so he picks it up when he’s ready (asynchronous) and delivers it to the customer.

var food = await waiter.BringFoodAsync(myOrder);

What you’re probably unaware of is that the .NET is so nice that it would always assume that you, as a customer, would want the same waiter that you gave the order to originally, to be the one that actually delivers the food (i.e. the result is delivered on the same thread that issued the await by default).

And that is nice, right? You know the waiter, maybe you had a little chat about the food, exchanged a few words, established a nice little context. When the execution continues (i.e. await is resolved), the context is restored as if there were no interruptions, on the same thread. And this is important in some cases — you might have things you rely on in your context, e.g. HttpContext.Current to name the most obvious one if you’re on the ASP.NET backend side.

What isn’t so nice, is that your food might be getting cold on the kitchen counter because your particular waiter is busy and currently serving other customers. And sometimes, he gets so busy he doesn’t get to deliver your food for quite some time. In those cases, you glance around, you see other waiters sitting around, doing their usual (nothing). Now, I bet you wish you hadn’t insisted on this particular waiter, right?

Here’s finally where that ConfigureAwait(false) comes into play— it’s saying: “I’m fine if any waiter picks up the food when it’s done”.

var food = await waiter.BringFoodAsync(myOrder)
.ConfigureAwait(false);

This would bring you food faster, because now any waiter that is available at the moment the kitchen finishes it can deliver it. If you’re not really there for chit-chat, you don’t really care that your context is lost (e.g. HttpContext.Current). But do note that in some cases, it might be the same waiter, in some — not (this can also be a reason for many elusive bugs, if you actually do care about the context, and you seem to sometimes have it, sometimes not).

Another drawback if you insist on your waiter is that you can create a deadlock if you’re too “pushy”… but, more on that later.

Let’s take a peek under the hood

How does this work? Let’s analyze it under the ASP.NET example.

You’ve probably realized that HttpContext.Current signature states that this is a static property — it holds all the important bits about the current request. You’ve also figured that there’s also many other requests going on at the same time, so this “static” property isn’t that much static, it’s more “static for your currently processing thread”. How does this works? When the request starts to be processed the meaning of .Current is assigned to the current thread — a “thread local storage” is used. It lives and breathes with the thread — if you start working on another thread, you’ll lose it. Then how doesn’t it get lost when you issue an await?

There’s a little thing called SynchronizationContext. It’s purpose is to execute methods on a certain thread. It’s mighty important for GUI apps, which have single thread for UI elements, and those elements don’t like to be touched by others — you’ll immediately get the exception if you try. So the SynchronizationContext is there to help — you tell it to Invoke a certain method and then you know it’ll be on the proper thread to make the change.

What this does on ASP.NET is that it also maintains the HttpContext.Current when your call is finished. So, if you have something like:

var food = await waiter.BringFoodAsync(myOrder);
EatTheFood(food);

This can (loosely) be interpreted as:

var task = waiter.BringFoodAsync(myOrder);
var currentSyncContext = SynchronizationContext.Current;
task.ContinueWith(
food =>
{
if (currentSyncContext == null)
EatTheFood(food);
else
currentSyncContext.Post(f => EatTheFood(f), food);
}
, TaskScheduler.Current);

When task finishes, the nice currentSyncContext that remembered the thread it was on (because it’s a local variable, not Thread local storage), will take it up and forward it to that same thread.

For more in-depth info, read this great piece.

On a side note, did you know that ASP.NET Core doesn’t have HttpContext.Current? How can it work without it? Because they dumped the thread storage thingies — if you want the current HttpContext, you go find it in Controller properties, not as a static accessor. This clears up a lot of things, everything becomes more clear, and forces you to state your dependencies explicitly (which is what should all of us be doing anyway).

Let’s use the opportunity of this fruitful abstraction model to explain some other stuff while we’re at it.

How does ConfigureAwait(false) solve deadlocks?

First of all, how can a deadlock occur?

Let’s say you have:

var food = waiter.BringFoodAsync(myOrder).Result;

This is basically saying: “Hey waiter! Here’s my order, but don’t go anywhere — you go tell the kitchen what to do, and come straight back here you hear me! I don’t want you messing around with other customers!”. What happens next is quite obvious — waiter does as told, goes to the kitchen, places the order, and comes straight back to the customer. As you’re staring intently at him, he’s unable to move away from your table… Even when the order is prepared, he can’t go away to fetch it. You’re in a deadlock (some examples with actual code can be found here).

And this situation gets resolved quite easily — you either don’t arrest your server and let him do his job (i.e. don’t use .Result or any blocking operation, like .Waitetc.), or, you don’t insist on your waiter to be the one that makes the delivery — use ConfigureAwait(false) when creating the kitchen order.

Why wrapping synchronous calls inside Task.Run() might be a bad idea in ASP.NET?

You’ve probably had dealt sometimes with some synchronous code, and had a perfect idea of improving it by making it run asynchronous (just wrap it in Task.Runright?). There’s numerous posts on the web saying “don’t do that!”. But… why not?

Let’s say our restaurant kitchen is of old type — the type that needs constant supervision if you want it done. So when waiter orders something, he has to sit at the counter and watch the chef prepare the meal or otherwise forget about it. Now, a new management comes in (the one that heard about async becoming trendy) and orders every waiter that when they order they should go about their business, and let some other waiter supervise the kitchen.

This is basically:

var food = await Task.Run(() => waiter.WaitForMeal(myOrder));

But, Task.Run is just another waiter (Thread in our analogy… yes, tasks are not threads, but let’s simplify this for the sake of clear explanation). This means that while your waiter is free, he had to hire another one to do his original job. Another one that now can’t serve his own customers and has to wait by the kitchen. Not only did you not optimize anything, but you’re also going to pay for the waiter communication (waiting for one another to be free, handing over the order etc.).

On the other hand, this might be favorable when the order is complex — let’s say you have more than one kitchen (one prepares Asian meals, one Italian), and you want to optimize time spent serving single-customers over the number of customers. When you make the order your waiter hires two additional waiters — he sends the first one to deal with Asian kitchen, the other one to wait by the Italian one. You’d get your meal twice as fast, but there’s a price: You now have three waiters dancing around one customer — might not be what you want if you’re running a busy restaurant.

Was this clear enough? Did it help? Let me know in the comments… also, do let me know if there’s anything I could tackle next as I really enjoyed explaining this one.

--

--

Veljko Zrnić

Father, husband, Software engineer, and more… I love exploring, learning, creating… and in general… making people happy