kasperhj
kasperhj

Reputation: 10482

What gets put on a thread pool thread in Task when using async/await?

Please consider the following code

    public static async Task<int> Answer()
    {
        await Task.Delay(1000);
        return 42;
    }

    static void Main(string[] args)
    {
        for (int j = 0; j < 20; j++)
        {
            Console.WriteLine(j +" " + Thread.CurrentThread.IsThreadPoolThread);
            if (j == 1)
            {
                new Task( async ()=>
                    {
                        int answer = await Answer();
                        Console.WriteLine(answer + " " + Thread.CurrentThread.IsThreadPoolThread);
                    }).Start();
            }

            Thread.Sleep(200);
        }

        return;

Can you guess what the CurrentThread.IsThreadPoolThread when printing answer is?

It appears that Task.Delay(..) is doing it, for if I remove await, then the answer is False. However, I can not seem to find that Delay actually puts stuff on the thread pool in the documentation.

This brings me to the more general question. What in Task does actually start up a thread pool thread besides Run(..)?

Edit:
Replacing new Task(...) with Ask()

    public static async void Ask()
    {
        int answer = await Answer();
        Console.WriteLine(answer + " " + Thread.CurrentThread.IsThreadPoolThread);
    }

yields the same result

Upvotes: 3

Views: 1812

Answers (1)

Stephen Cleary
Stephen Cleary

Reputation: 456507

As I describe in my async / await intro post, by default await will capture the current "context" and use that to execute the continuation (the code after the await). This "context" is SynchronizationContext.Current unless it is null, in which case it is TaskScheduler.Current.

I also describe using async on Console apps on my blog. Console apps do not provide a SynchronizationContext, so the "context" falls back to TaskScheduler.Current. Since the currently-executing Task is running on the thread pool, TaskScheduler.Current is the same as TaskScheduler.Default, which is the thread pool task scheduler.

This is why your code is resuming on a thread pool thread. It's the correct behavior.

Upvotes: 3

Related Questions