Gabe's Code

Stuff I've learned along the way



profile for Gabriel Luci at Stack Overflow, Q&A for professional and enthusiast programmers

.NET: Don’t use ConfigureAwait(false)

Except for library code. I’ll get to that, but hear me out.

I’ve seen this advice all over:

As a general rule, ConfigureAwait(false) should be used for every await unless the method needs its context.

It’s even what Stephen Cleary (a Microsoft MVP) says in his Async and Await article:

A good rule of thumb is to use ConfigureAwait(false) unless you know you do need the context.

Stephen definitely knows his stuff, and I agree that the advice is technically accurate, but I’ve always thought that this is bad advice for two reasons:

  1. Beginners, and
  2. Maintenance risk


First, it’s bad advice for beginners because synchronization context is a complex subject. If you start learning async/await by being told that “ConfigureAwait(false) should be used for every await unless the method needs its context”, but you don’t even know what “context” is and what it means to “need it”, then you don’t know when you shouldn’t use it, so you end up always using it. That means you can run into bugs that will be very difficult to figure out unless you happen to learn that, yes, you did actually need that “context” thing and this magical “ConfigureAwait” thing made you lose it. You can lose hours trying to figure that out.

For applications of any kind, I believe the advice really should be the opposite: Don’t use ConfigureAwait(false) at all, unless you know what it does and you have determined that you absolutely don’t need the context after that line. However…

Maintenance Risk

Determining you don’t need the context can be either simple, or quite complex depending on what methods are called after. But even then - and this is the second reason I disagree with that advice - just because you don’t need the context after that line right now, doesn’t mean some code won’t be added later that will use the context. You’ll have to hope that whoever makes that change knows what ConfigureAwait(false) does, sees it, and removes it. Using ConfigureAwait(false) everywhere creates a maintenance risk.

A Note on Synchronization Context

A couple simple examples of needing the synchronization context are:

  1. Changing a UI control in a Windows Forms or WPF project
  2. Accessing HttpContext.Current in an ASP.NET (not Core) project

Doing one of those things after the use of await with ConfigureAwait(false) might throw an exception. In the case of a Windows Forms or WPF app, a UI control can only be changed by the thread that created it. The synchronization context ensures that the continuation runs of the same thread. However, if you use ConfigureAwait(false), you tell it that you don’t care where it resumes, so it might resume on the same thread, or might not. Using ConfigureAwait(false) leaves it up to chance whether an exception is thrown, making your debugging more difficult.

If you’d like to read more about synchronization context, you can read the article What is Synchronization Context? by Hamid Mosalla. If you’d like to dig even deeper and see some of the code that goes behind a synchronization context, you can read the article Exploring the async/await State Machine – Synchronization Context by Vasil Kosturski.


This is what another Stephen, Stephen Toub (a Microsoft employee), recommends in the ConfigureAwait FAQ under the subheading “When should I use ConfigureAwait(false)?”:

When writing applications, you generally want the default behavior (which is why it is the default behavior). … This leads to the general guidance of: if you’re writing app-level code, do not use ConfigureAwait(false)

In my own application code, I don’t bother trying to figure out where I can and can’t use it. I just ignore that ConfigureAwait exists. Sure, there can be a performance improvement by using it where you can, but I really doubt that it will be a noticeable difference to any human, even if it is measurable by a timer. I don’t believe the return on investment is positive.


The only exception to this is when you’re writing libraries (code compiled into a DLL that will be used in other applications), as Stephen Toub points out in his article:

if you’re writing general-purpose library code, use ConfigureAwait(false)

That’s for two reasons:

  1. A library has no idea about the context of the application it’s being used in, so it can’t use the context anyway, and
  2. If the person using the library decides to wait synchronously on your asynchronous library code, it could cause a deadlock that they cannot change because they can’t change your code. (ideally, they shouldn’t do that, but it can happen)

And keep in mind that it’s not always enough to use ConfigureAwait(false) on the first await and not the rest. Use it on every await in your library code. Stephen Toub’s article under the heading “Is it ok to use ConfigureAwait(false) only on the first await in my method and not on the rest?” says, in part:

If the await task.ConfigureAwait(false) involves a task that’s already completed by the time it’s awaited (which is actually incredibly common), then the ConfigureAwait(false) will be meaningless, as the thread continues to execute code in the method after this and still in the same context that was there previously.

It may seem arrogant of me to disagree with Stephen Cleary on this subject: he’s well respected. However, I first wrote this as an answer on Stack Overflow. Stephen Cleary commented on that post saying:

I believe I will update that async blog post. For the last several years, I have also recommended only using ConfigureAwait(false) for library code.

So it’s good to know we agree on that now.


Really good post. I think if you can update it, specifically for this:

but you don’t even know what “context” is and what it means to “need it”

I think is a really good opportunity to tell beginners “what is a context” and what “need it” means (for general scenarios).

That is a good idea. I may not fully explain it here, but I’ll at least add a link to another article explaining it.

it doens’t seem like you got around to it, do you have a link to a good article explaining it?

Sorry about the wait. I added a section on the synchronization context. It’s very brief, but I linked to a couple other articles with more information.

Good article, also agree with Daniel, I would like to know when I need the context, so I can decide wisely when to use ConfigureAndAwait with false or true.
There should be a better way of deciding than just ‘application’ or ‘library’ code.

Sorry I never replied sooner.

In library code, you can never use the context, so you should always use ConfigureAwait(false).

In application code, whether you need the context depends entirely on your code - you might need it, or you might not. Since you wrote this, I added a section on synchronization context.

But my point in this article is that you will spend more time than it’s worth trying to use ConfigureAwait(false) everywhere you can, and you will create a maintenance risk if you use it, then later someone changes the code after that to use the context and you end up with a bug. This is why I recommend that you never use it in application code. You will never notice a difference in speed.

Aleksandar on

The text is missing to explain default behavior in .NET Framework and .NET Core which is the key to understand the difference in what you should or should not use. Second there has to be an explanation over what is going on in .NET Standard or .NET Core or .NET Framework, which one you can mix with which one and where the problem of using ConfigureAwait even appears.
I have not seen such an article yet.

It comes down to whether there is a synchronization context you can actually use. In any library code, regardless of which framework you’re using, there will never be a context you can use, which is why you would always use ConfigureAwait(false) in library code.

With ASP.NET, there is a synchronization context in .NET Framework, but none in ASP.NET Core.

In any UI application, regardless of framework, there will always be a synchronization context since UI updates must happen on a single thread.

This is why you won’t see different advice for different frameworks. Async/await is a feature of the C# language, not the framework. Whether ConfigureAwait(false) does anything or not depends on whether there is a synchronization context, which, in turn, depends on the type of application. The only type of application where the framework changes whether there is a synchronization context is in ASP.NET.

Hardy Hobeck on

Actually, I’m not agree with your arguments. The semantic meaning of defining ConfigureAwait(false) is that you don’t care about the synchronization context. If you skip the line then the default value ConfigureAwait(true) applies which means you care about the context (because this was the decision of Microsoft). Code should be clear and the intention needs to be expressed so everyone is knowing what the code does. If you don’t do this then the code is not accurate and wrong in my opinion. I have figuring out so many deadlocks in production because of this issue (of course I know not mixing synchronous and asynchronous calls fix this but in real world application no one can give this assurance ;)). Thus, using ConfigureAwait(false) is a second line of defense (write code which is defensive).

  1. Beginnners:
    Not using a feature or using a feature with a different intention because some beginner could use it somewhere else in a wrong way is not a good advice.

  2. Maintenance Risk
    Normally if the code is self-explained without any separation of concern violations, then it should fit on half of the screen. Means a person who maintaining the code will see it. Anyway, instead of explaining a fictive scenario in future you should explain problems which happening in present.

Barry Hynum on

I needed the VS2019 DTE2 for IDE events. I used package.GetServiceAsync(GetType(DTE)).ConfigureAwait(false))… the Default. When I did a breakpoint on that line then stepped through, my DTE events all worked. W/o the breakpoint it failed. I changed ConfigureAwait(false) to True. All worked fine. This may be an “obvious” to the experts. It was not for me.

Leave a comment

Your email address is used to display your Gravatar, if applicable, and subscribe you to replies using the Mailgun web service, which you are free to unsubscribe from when you get any emails. Your email address will not be displayed publicly or shared with anyone else.
Comments are moderated. Your comment will be reviewed by a human before being posted to this page. Any comment made for the purpose of advertising will not be approved.