1

I was trying to understand a bit about which threads things get run on. My understanding was, when we use await, the original context is captured and the continuation will run on the original thread. This not proving to me the case for me, see below code.

Is this because I am using .NET 6 and context don't matter in .NET Core/.NET 6 anymore? (EDIT: I was running this as console app)

await DoSomething();
public async Task DoSomething()
{
    Console.WriteLine("Starting thread: " +Thread.CurrentThread.ManagedThreadId);
    var listofProcessor = new List<Processor> {
        new ("Processor"), new ("Processor1")
    };

    try
    {
        var tasks = listofProcessor.Select(x => x.Process());

        Console.WriteLine($"Before Task.WhenAll  {Thread.CurrentThread.ManagedThreadId}");
        await Task.WhenAll(tasks);
        Console.WriteLine($"After Task.WhenAll  {Thread.CurrentThread.ManagedThreadId}");
    }
    catch (Exception e)
    {
        Console.WriteLine("Exception caught");
    }
}
   
public class Processor 
{
    private string _name; 
    public Processor(string name) => _name = name;

    public async Task<string> Process()
    {
        Console.WriteLine($"{_name}.BeforeAwait {Thread.CurrentThread.ManagedThreadId}");
        await Task.Delay(100);
        Console.WriteLine($"{_name}.AfterAwait {Thread.CurrentThread.ManagedThreadId}");

        return "hellow";
    }
}

I would expect, Console.WriteLine($"After Task.WhenAll xxx) to be run on the original thread. But that is not happening. The result is:

Starting thread: 1
Before Task.WhenAll  1
Processor.BeforeAwait 1
Processor1.BeforeAwait 1
Processor.AfterAwait 9
Processor1.AfterAwait 6
After Task.WhenAll  6
Guru Stron
  • 102,774
  • 10
  • 95
  • 132
stuck_inside_task
  • 273
  • 1
  • 2
  • 6

2 Answers2

2

the original context is captured and the continuation will run on the original thread.

No. A context is not a thread. The context is captured and then the continuation runs on that context.

For Console applications, the context is the thread pool context, which schedules work onto the thread pool. This is true for all versions of .NET.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • *"For Console applications, the context is the thread pool context"* -- Technically this is not true. The context is simply `null`, not the thread pool context ([demo](https://dotnetfiddle.net/5vUPRn)). Console applications don't have a mechanism in place that actively redirects the `await` continuations to the thread pool. If a `Task` completes on a non-thread-pool thread, the continuation will run on that thread ([demo](https://dotnetfiddle.net/iWQoCU)). – Theodor Zoulias Jul 27 '23 at 01:32
  • Yes and no. Technically there is no context. But no context is treated just like the thread pool context for practical purposes. – Stephen Cleary Jul 27 '23 at 01:48
  • In practice all built-in asynchronous methods complete on the thread pool, which gives the impression that the thread pool is the context. But this is coincidental. It can be easily proved experimentally that there is no context, and the `await` continuations run on whatever thread the associated tasks completed. – Theodor Zoulias Jul 27 '23 at 02:36
2

My understanding was, when we use await, the original context is captured and the continuation will run on the original thread.

Yes, if synchronization context performing such synchronization is present then it would have such effect. Example would be the UI context which is single-threaded and runs the continuations on the UI thread. Console apps do not have synchronization context (or have the default - thread pool one) so running the continuation on the same thread would be simply coincidental. Actually I can't reproduce the behaviour with .NET Framework - check the demo fiddle.

Is this because I am using net 6 and context don't matter in .NET Core/.NET 6 anymore?

The "main" thing which changed in .NET (Core) since .NET Framework is that ASP.NET Core does not have a synchronization context (note that the "classic" ASP.NET synchronization context is not actually tied to a specific thread like the UI one, but it does only allow one thread in at a time).

Guru Stron
  • 102,774
  • 10
  • 95
  • 132