When awaiting tasks in C#, you have the option to configure how the continuation behaves – whether it uses the Synchronization Context or not.

This can be pretty important, especially when awaiting tasks in code that was initialized from a UI thread, which has a Synchronization Context. Poorly written code can easily result in a deadlock, not to mention a serious perf hit. For example:

class MyWindow : Window
{
  private void MyButton_Click(object sender, EventArgs e)
  {
    DoSomething().Wait();
  }

  private async Task DoSomething()
  {
     await Task.Run(() => { });
  }
}

This is obviously bad, calling Wait() on the UI thread. But it’s easy to imagine more subtle ways this could happen inside library code. That’s why the best practice is to always use ConfigureAwait(continueOnCapturedContext: false) in library code. I really wish they’d split that into two separate methods. I also wish they’d made the default to be false.

I’ve recently come to the conclusion that it’s best to always specify ConfigureAwait everywhere lest you forget. So I wrote up a small ReSharper plugin (my first) that checks for it and also provides a quick-fix (ReSharper provides an awesome extensibility model):

ConfigureAwait Checker for ReSharper

Download it from the ReSharper extensions gallery: http://resharper-plugins.jetbrains.com/packages/ConfigureAwaitChecker.

Update: Now supporting ReSharper 9 (different package): https://resharper-plugins.jetbrains.com/packages/ConfigureAwaitChecker.v9

Tagged with:
 

4 Responses to To ConfigureAwait or not to ConfigureAwait?

  1. Nemo says:

    Nice article (came here from R#’s rss blog search). Regarding the “I really wish they’d split that into two separate methods.” you can remedy that with extension methods, this is what I use in my libs:

    using System.Diagnostics;
    using System.Runtime.CompilerServices;
    using Torola.ToolsLibrary.Annotations;

    // ReSharper disable once CheckNamespace
    namespace System.Threading.Tasks
    {
    [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]
    public static class TaskExtensions
    {
    [Pure]
    [DebuggerStepThrough]
    public static ConfiguredTaskAwaitable ContinueOnCapturedCtx([NotNull] this Task task)
    {
    return task.ConfigureAwait(true);
    }

    [Pure]
    [DebuggerStepThrough]
    public static ConfiguredTaskAwaitable ContinueOnNewCtx([NotNull] this Task task)
    {
    return task.ConfigureAwait(false);
    }

    [Pure]
    [DebuggerStepThrough]
    public static ConfiguredTaskAwaitable ContinueOnCapturedCtx([NotNull] this Task task)
    {
    return task.ConfigureAwait(true);
    }

    [Pure]
    [DebuggerStepThrough]
    public static ConfiguredTaskAwaitable ContinueOnNewCtx([NotNull] this Task task)
    {
    return task.ConfigureAwait(false);
    }
    }
    }

    If you can think of better naming for these methods let me know, it was a brain teaser for me lol

    • aelij says:

      Thanks, I’ve considered this, but my problem is that it’s nonstandard. When someone skims through a piece of code for the first time it might not be immediately obvious what this method is doing. For now I’ll stick with ConfigureAwait().

  2. Shredder says:

    Useful stuff!

    Making it false by default would also have been more consistent with the previous model (before async/await), since ContinueWith only posts to the synchronization context if you explicitly use TaskScheduler.FromCurrentSynchronizationContext

Set your Twitter account name in your settings to use the TwitterBar Section.