11

I've read the advice many times from people smarter than me, and it has few caveats: Always use ConfigureAwait(false) inside library code. So I'm fairly certain I know the the answer, but I want to be 100%. The scenario is I have a library that thinly wraps some other asynchronous library.

Library code:

public async Task DoThingAsyc() {
    // do some setup
    return await otherLib.DoThingAsync().ConfigureAwait(false);
}

Application code:

// need to preserve my synchronization context
await myLib.DoThingAync();
// do I have my context here or did my lib lose it?
Todd Menier
  • 37,557
  • 17
  • 150
  • 173
  • 1
    `ConfigureAwait` doesn't return a `Task`, but a `ConfiguredTaskAwaitable`. `DoThingAsync` can't look like it does in the example. – i3arnon Sep 12 '15 at 20:04
  • @i3arnon so I guess that wouldn't have even built. Better now? (No compiler on me atm) – Todd Menier Sep 12 '15 at 21:07
  • Yes, that would compile and the `ConfigureAwait` wouldn't affect the caller. – i3arnon Sep 12 '15 at 21:11
  • Thanks for the help. Given that, I think the question becomes "should" I make this change, which I've asked separately [here](http://stackoverflow.com/q/32551534/62600). – Todd Menier Sep 13 '15 at 15:56

3 Answers3

10

No.

The capturing of the SynchronizationContext happens on await. ConfigureAwait configures the specific await.

If the application calls a library's async method and awaits it the SC is captured on the spot regardless of what happens inside the call.

Now, because the async method's synchronous part (which is the part before the first await) is executed before a task is returned to be awaited, you can mess around with the SynchronizationContext there, but ConfigureAwait doesn't do that.


In your specific example you seem to be returning the result of ConfigureAwait from the async method. That can't happen because ConfigureAwait returns the ConfiguredTaskAwaitable struct. If however we change the method return type:

public ConfiguredTaskAwaitable DoThingAsyc() 
{
    return otherLib.DoThingAsync().ConfigureAwait(false);
}

Then awaiting it will indeed affect the calling code's await behavior.

i3arnon
  • 113,022
  • 33
  • 324
  • 344
  • I'm not saying you're wrong, but I find this part confusing: *`ConfigureAwait` configures the specific `await`*. The library's `Task DoThingAsyc` is a synchronous method that returns a task and does not use `await`. Should this then mean that the `ConfigureAwait(false)` *does* affect the user's `await`, because calling such synchronous method with `await myLib.DoThingAync();` is no different to directly calling `await otherLib.DoThingAsync().ConfigureAwait(false);`, which *should* affect the `await` according to that wording? – GSerg Sep 12 '15 at 20:02
  • 3
    @GSerg [`ConfigureAwait` doesn't return a `Task`](http://stackoverflow.com/questions/32542861/can-configureawaitfalse-in-a-library-lose-the-synchronization-context-for-the/32543035?noredirect=1#comment52943229_32542861) – i3arnon Sep 12 '15 at 20:05
2

Example from http://blogs.msdn.com/b/pfxteam/archive/2012/01/20/10259049.aspx

... logically you can think of the following code:

await FooAsync();
RestOfMethod();

as being similar in nature to this:

var t = FooAsync();
var currentContext = SynchronizationContext.Current;
t.ContinueWith(delegate
{
    if (currentContext == null)
        RestOfMethod();
    else
        currentContext.Post(delegate { RestOfMethod(); }, null);
}, TaskScheduler.Current);

which means you should have your context back after the await myLib.DoThingAync(); call.

m0sa
  • 10,712
  • 4
  • 44
  • 91
2

If used inconsistently in the logical chain of async calls, ConfigureAwait(false) may add redundant context switches (which usually means redundant thread switches). This may happen in the presence of synchronization context, when some async calls on the logical stack use ConfigureAwait(false) and some don't (more here).

You still should use ConfigureAwait(false) in your code, but you may want to peek into the 3rd party code you're calling and mitigate any inconsistency with something like this:

public async Task DoThingAsyc() {
    // do some setup
    await Task.Run(() => otherLib.DoThingAsync()).ConfigureAwait(false);
    // do some other stuff
}

This would add one extra thread switch, but might potentially prevent many others.

Moreover, if you're creating a really thin wrapper like you showed, you may want to implement it like below, without async/await at all:

public Task DoThingAsyc() {
    // do some setup
    return otherLib.DoThingAsync();
}
Community
  • 1
  • 1
noseratio
  • 59,932
  • 34
  • 208
  • 486
  • 1
    Your example at the bottom is exactly how my [library](https://github.com/tmenier/Flurl) does it in dozens of places today, and the crux of the question is whether or not I should change these calls to use `ConfigureAwait(false)`. As @i3arnon reminded me, `ConfigureAwait` returns a `ConfiguredTaskAwaitable`, which I would need to wrap back into a `Task`, which presumably carries *some* overhead. That along with the potential thread switching you mention makes me wonder, is it worth it or should I just leave it the way it is? – Todd Menier Sep 13 '15 at 15:18
  • 1
    Actually I think this warrants a [new question](http://stackoverflow.com/q/32551534/62600). – Todd Menier Sep 13 '15 at 15:57
  • @ToddMenier, basically you only need `await` if you have a continuation code (like `// do some other stuff`above). Otherwise, there's no point in using it, unless you specifically want the `async` semantic of exception prorogation (more [here](http://stackoverflow.com/a/21082631/1768303)). – noseratio Sep 13 '15 at 20:49