2

I would like to create/run a task without capturing the current execution context. Is this possible?

Consider the following sample:

private static readonly AsyncLocal<object> AsyncLocal = new AsyncLocal<object>();

[TestMethod]
public void TaskRunCapturesContext()
{
    AsyncLocal.Value = 1;
    var task = Task.Run(static async () =>
        {
            await Task.Delay(1).ConfigureAwait(false);
            Console.WriteLine(AsyncLocal.Value); // Prints "1", expected is "" <-- Problem HERE
            AsyncLocal.Value = 2;
            Console.WriteLine(AsyncLocal.Value); // Prints "2", expected is "2"
        });

    Task.WaitAll(task);
    Console.WriteLine(AsyncLocal.Value); // Prints "1", expected is "1"
}

Is it possible to make the first Console.WriteLine(AsyncLocal.Value); print "" instead of "1" (because AsyncLocal.Value is null/not initialized)?

I also played around with all overloads of the more explicit Task.Factory-method, but I couldn't find a solution. Form the source it can also be seen that Task.Run(Action) translates into a call to Task.InternalStartNew(null, action, null, ..) which explicitly passes null as state-argument, so I dont see a way I could be more explicit.

Thanks in advance

BudBrot
  • 1,341
  • 2
  • 24
  • 44
  • 1
    Note that `task.ConfigureAwait(false);` does not mutate the `Task`. Rather, it returns an awaiter which doesn't capture the current SynchronizationContext. You need to await the thing which `ConfigureAwait` returns – canton7 Mar 07 '22 at 15:06
  • @canton7: You are right, thanks. I removed it because it should not matter at all in ASP.NET Core. – BudBrot Mar 07 '22 at 15:10
  • 3
    [`ExecutionContext.SuppressFlow`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.executioncontext.suppressflow?view=net-6.0)? – canton7 Mar 07 '22 at 15:14
  • 1
    @canton7: On point, thanks. Exactly what i wanted. – BudBrot Mar 07 '22 at 15:24

1 Answers1

3

Thanks to @canton7's comment the answer is rather simple: You can prevent the the ExecutionContext from flowing by using ExecutionContext.SuppressFlow.

Corrected above sample:

private static readonly AsyncLocal<object> AsyncLocal = new AsyncLocal<object>();
[TestMethod]
public void TaskRunNoLongerCapturesContext()
{
    AsyncLocal.Value = 1;
    using (ExecutionContext.SuppressFlow()) // <-- THIS
    {
        var task = Task.Run(static async () =>
            {
                await Task.Delay(1).ConfigureAwait(false);
                Console.WriteLine(AsyncLocal.Value); // Prints "", expected is "" <-- Hurrah
                AsyncLocal.Value = 2;
                Console.WriteLine(AsyncLocal.Value); // Prints "2", expected is "2"
            });

        Task.WaitAll(task);
    }

    Console.WriteLine(AsyncLocal.Value); // Prints "1", expected is "1"
}
BudBrot
  • 1,341
  • 2
  • 24
  • 44