-2

In the article “Async and Await” by Stephen Cleary it is said that to run awaitable on the thread pool you need to call ConfigureAwait(false) on that awaitable. That somehow does not match my experience. I have created a little app that I think proves that it is not necessary to call ConfigureAwait to execute awaitable on a separate thread.

I have used log4net tool for logging purpose. Without using a ‘ConfigureAwait’ method the awaitable executes on a different thread ([3]) then the UI ToolStripButton1_Click call(UI thread is [1] and pool thread is [3] – code and log output is attached below).

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
    private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

    public Form1()
    {
        InitializeComponent();
    }

    private async void ToolStripButton1_Click(object sender, EventArgs e)
    {
        await TestAsync();
    }
    private async Task TestAsync()
    {
        log.Info("Button clicked before Run Sleep task. Expect to run this from UI thread");
        Task t = Task.Run( () =>
        {
            log.Info("Button clicked from Run before Sleep. Expect to run this from a pool thread");
            Thread.Sleep(1000 * 5);
            log.Info("Button clicked from Run after Sleep. Expect to run this from a pool thread");
            return true;
        });//.ConfigureAwait(continueOnCapturedContext: false);
        await t;
        log.Info("Button clicked after Run. Expect to run this from UI thread"); // Expect to run this in UI thread
    }
}

}

Log output looks like this:

2020-01-31 19:57:14,805 [1] INFO WindowsFormsApp1.Form1[MoveNext] - Button clicked before Run Sleep task. Expect to run this from UI thread

2020-01-31 19:57:14,835 [3] INFO WindowsFormsApp1.Form1[b__3_0] - Button clicked from Run before Sleep. Expect to run this from a pool thread

2020-01-31 19:57:19,837 [3] INFO WindowsFormsApp1.Form1[b__3_0] - Button clicked from Run after Sleep. Expect to run this from a pool thread

2020-01-31 19:57:19,839 [1] INFO WindowsFormsApp1.Form1[MoveNext] - Button clicked after Run. Expect to run this from UI thread

I did not call ConfigureAwait(false) and awaitable is executed on the thread pool.

Chris Catignani
  • 5,040
  • 16
  • 42
  • 49
Rejkid
  • 109
  • 1
  • 6
  • 4
    ConfigureAwait is for the continuation of the await and where it should continue execuction. – Rowan Smith Jan 31 '20 at 21:04
  • 4
    By default `await` captures the current synchronization context and execution resumes on it (UI thread in your case). `ConfigureAwait(false)` disables this, and execution continues on thread pool. `Task.Run` will always runs on thread pool thread (it is different from UI thread). You've probably mixed the async code execution and its continuation after resuming – Pavel Anikhouski Jan 31 '20 at 21:06
  • Here is the article I believe: https://blog.stephencleary.com/2012/02/async-and-await.html – nollidge Jan 31 '20 at 21:22
  • In my understanding, `ConfigureAwait(false)` does not affect the context that the awaitable is run on, it affects the context of the *continuation* (i.e. everything AFTER the `await`). – nollidge Jan 31 '20 at 21:29
  • The article you are referring to is 8 years old. – NotTheBatman Jan 31 '20 at 21:37
  • I think you can have a look at https://medium.com/bynder-tech/c-why-you-should-use-configureawait-false-in-your-library-code-d7837dce3d7f which provides reasons to use `ConfigureAwait(false)`. – MKR Jan 31 '20 at 21:45
  • From the answer in the marked duplicate: _"Await will post continuation (the rest of the method after await) to current context (SynchronizationContext.Current) **if it's present** and if you did not use ConfigureAwait false"_. Assuming you've read the volumes of documentation available to you, you misunderstood what you read. Note that you don't get resumed in the current context, if no such context exists. The default is thread pool, just as you have seen. – Peter Duniho Jan 31 '20 at 22:00
  • See related: https://stackoverflow.com/questions/18670617/decide-when-to-use-configureawaitfalse, https://stackoverflow.com/questions/28410046/revisiting-task-configureawaitcontinueoncapturedcontext-false, https://stackoverflow.com/questions/48809379/contradictory-advice-regarding-async-await-in-mvc, among hundreds of other similar questions on Stack Overflow. – Peter Duniho Jan 31 '20 at 22:00
  • Yes, await/async can be confusing. There is a lot of complexity under the hood to make it look and feel as simple as possible. In practice most of the time you just need to write the simplest and most direct syntax, and it just works. – Theodor Zoulias Jan 31 '20 at 22:01

1 Answers1

1

ConfigureAwait is for the continuation of the await and where it should continue execution when the await is completed. It's not for which threadpool the awaitable runs on.

So when your await Task.Run() completes, ConfigureAwait() determines if the continuation should run on the Calling Context or the threadpool.

If you set it to ConfigureAwait(false) then the finale log.Info in your example would execute on the same threadpool as the previous two log.Info's

In the article “Async and Await” by Stephen Cleary it is said that to run awaitable on the thread pool you need to call ConfigureAwait(false) on that awaitable.

You didn't give a link to the article but I would be highly surprised if Stephen Cleary got this wrong.

Rowan Smith
  • 1,815
  • 15
  • 29
  • 1
    Yep. Sorry about the link. You pointed out what I was missing - "ConfigureAwait is for the continuation of the await...". Thank you very much. I didn't think Stephen Cleary was wrong - I was trying to find out what I missed. – Rejkid Feb 02 '20 at 03:21