77

As you know, it it a good idea to call Task.ConfigureAwait(false) when you are waiting on a task in a code that does not need to capture a synchronization context, because it can cause deadlocks otherwise.

Well, how often do you need to capture a synchronization context? I my practice, very rarely. In most situations I am working with "library" code that pretty much forces me to use Task.ConfigureAwait(false) all the time.

So my question is pretty simple: why Task.ConfigureAwait(false) is not the default option for a task? Would not it be much better to force "high-level" code to use Task.ConfigureAwait(true)? Is there a historical reason for it, or am I missing something?

RX_DID_RX
  • 4,113
  • 4
  • 17
  • 27
  • 9
    You realize that the *vast* majority of programmers aren't programming reusable libraries, they're programming application code. You're asking why C# didn't make the decision that was right for you, and instead made the decision that was right for the *vast* majority of its users. – Servy Oct 31 '14 at 18:46
  • 4
    Oh, and it's worth noting that users shouldn't be blocking the sync context in an asynchronous application at all. The solution to that problem is to not do that, not to use `ConfigureAwait(false)` everywhere. – Servy Oct 31 '14 at 18:46
  • 3
    One of the biggest incentives for the async/await feature is UI code. It's great used with WinForms and WPF, but it's particularly important to support Microsoft's desire for Windows Store apps to remain responsive. Clearly programmers had a hard time getting this right in the past, so offering a new language feature to make it easy was important. And it makes sense for the default behavior to be consistent with that goal, as opposed to trying to protect against programmers who are misusing it anyway. – Peter Duniho Oct 31 '14 at 18:52
  • 5
    I've been pondering this, and I wonder if it should've been a different keyword. i.e. `await withcontext` Maybe not, as it adds another keyword in the mix and can be confusing for developers, and harder to market (i.e. async/await! vs async/await/await withcontext!) – contactmatt Oct 04 '16 at 19:11
  • 3
    Fyi, there is a useful resharper extension [ConfigureAwait Checker](https://resharper-plugins.jetbrains.com/packages/ConfigureAwaitChecker.v9) which forces you to use `ConfigureAwait` whenever you use `await`. It auto completes with `.ConfigureAwait(false)` if desired. – Tim Schmelter Mar 15 '17 at 12:09
  • 1
    In an environment where almost all features need to be executed on the main thread, usually interactive applications or games, it would also be tedious to suffix everything with `ConfigureAwait(true)`. I don't really know, perhaps this reason outweighed the possibility of generating deadlocks... You may want to create a couple of extension methods to decrease verbosity. `public static ConfiguredTaskAwaitable CA(this Task task, bool continueOnCapturedContext = false) => task.ConfigureAwait(continueOnCapturedContext);` – fibriZo raZiel Oct 27 '20 at 11:02

3 Answers3

19

Most code that works with .ConfigureAwait(false) also works, although subobtimal, with .ConfigureAwait(true). Yes, not all code, but still most. The current default lets the highest percentage of code work without tinkering with settings that an average programmer might not understand.

A different default would just lead to thousands of questions about why the code does not work, and worse yet, thousands of answers in the form of "Microsoft sucks, they make you write Control.CheckForIllegalCrossThreadCalls = false; in every program. Why isn't that the default?" rather than actually adding the appropriate .ConfigureAwait(true) calls.

Community
  • 1
  • 1
  • 2
    @rianjs The whole point of my answer is that many programmers don't know what they're doing. The point is that the design choice is made because of it. Removing that means the rest of my answer no longer makes sense. –  May 23 '17 at 15:43
16

Look at the second example solution from that link:

public async void Button1_Click(...)
{
  var json = await GetJsonAsync(...);
  textBox1.Text = json;
}

public class MyController : ApiController
{
  public async Task<string> Get()
  {
    var json = await GetJsonAsync(...);
    return json.ToString();
  }
}

If the default behaviour was ConfigureAwait(false), the textBox1.Text = json; statement would execute on a random thread pool thread instead of the UI thread.

Both snippets look like code someone could reasonably write, and by default one of them has to be broken. Since deadlocks are a lot less dangerous and easier to detect than thread-unsafe accesses, picking ConfigureAwait(true) as the default is the more conservative choice.

Tavian Barnes
  • 12,477
  • 4
  • 45
  • 118
  • 3
    The point of the proposal would be that you'd need to have `ConfigureAwait(true)` (or some equivalent) every time you wanted code to run in the UI thread after an `await`, not that you couldn't do it at all. – Servy Oct 31 '14 at 19:03
  • 3
    @Servy Very true, but when picking a default, the tradeoff is between possible deadlocks and possible thread-unsafe accesses. Deadlocks are a better (more conservative) default. – Tavian Barnes Oct 31 '14 at 19:15
  • 1
    Then discuss that in your answer. As it is your answer doesn't actually answer anything. The whole point of the OP's question is that he *wants* the code you showed to not execute the continuations on the UI thread. You saying that they wouldn't is in no way an answer. – Servy Oct 31 '14 at 19:18
  • @Servy Agreed, I added some further explanation. – Tavian Barnes Oct 31 '14 at 19:34
  • 2
    @Servy - the above code is simply wrong if you call it with .ConfigureAwait(false), it's not a matter of preference. In UI contexts, you can't do that, because you may not return to the UI thread. Therefore this is a great answer as to why the default is the way it is. Obviously OP isn't writing UI code, so prefers to have the default as false, but I agree with Tavian this is the safer default. – Stephen Holt Jun 08 '17 at 10:02
  • @StephenHolt Yes, the code here needs to capture the context. Whether the context is captured, or not captured, by default is *entirely* a matter of preference. It would *always* be possible to capture it or not capture it, it would *purely* be a question of which requires doing nothing and which requires calling a method. The option that a given person wants is going to be personal preference. The statement that one entirely broken option is better than some entirely different broken choice is simply not true. Saying your preference is one option doesn't make it not preference. – Servy Jun 08 '17 at 13:08
11

Just because your typical use case requires ConfigureAwait(false), it doesn't mean that it is the "correct" or most used option.

One of the things async/await is designed for, is to write responsive GUI programs. In such cases, returning to the UI thread after offloading some work to a Task is critical, since UI updates can only happen from the main thread on most Windows GUI platforms. Async/await helps GUI developers do the right thing.

This is not the only example where the default option makes better sense. I can only speculate, but I would suspect that the decision for the ConfigureAwait default is based on making sure async works with as little friction as possible, for the use cases that Microsoft anticipates it will be used for the most. Not everyone writes frameworks.

driis
  • 161,458
  • 45
  • 265
  • 341
  • 16
    Sorry but that's nonsense. The vast majority of modern development is NOT on Windows GUI or IIS application servers. We have this need to specify ConfigureAwait(false) foisted on use because of that bias from last century. BTW, what does it mean? Do not configure await? It's an asinine name because there is only ONE option and it's essentially no thread affinity. Somebody had the bright idea to call that configuring the await instead of calling it affinity(false) or something more sensible. – Rick O'Shea Oct 25 '18 at 17:51
  • The reason it's no the default is to prevent a tsunami of crashing applications as IIS apps (and to some extent GUI apps) don't mysteriously fail right out of the gates. – Rick O'Shea Oct 25 '18 at 17:53
  • I think you're right. People separate their business logic from platform code, e.g. by IoC/DI. This means the business code semantically has no right to decide whether or not it can continue on any context because by definition it has no clue how the dependencies work or even which UI it uses. This is especially true if you have shared business logic between websites, apis, fat clients and tasks. Whenever you're saying `ConfigureAwait(false)` you say "this code knows its environment". If this is the default in all of your code, you're probably thinking about architecture in a really bad way. – Oliver Schimmer Aug 28 '20 at 16:36
  • 1
    Moreover, I'd contest even if you're writing in .NET Core which does not have a dedicated context, it would be semantically wrong in many cases to use `ConfigureAwait(false)`, because you would just make assumptions in your business code about the behaviour of your platform. If your framework does not need it, you lose the overhead anyway. The only reasonable place in application code to blindly use `ConfigureAwait(false)` imo would be something like the DAL, where you have no interaction and program directly against a dependency like Entity Framework, which you know does not need a context. – Oliver Schimmer Aug 28 '20 at 17:00