3

I read the Microsoft document about async and await. It says:

The async and await keywords don't cause additional threads to be created.

But I run the below code

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain
    {
        static void Main()
        {
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
            Task task = Test();
            task.Wait();
        }

        public async static Task Test()
        {
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
            await Task.Delay(1000);
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
        }

    }
}

The result is

1
1
4

It is obvious that the

await Task.Delay(1000);
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);

is run in another thread. Why does the document says there is no thread created?

I have read the previous question Does the use of async/await create a new thread?, but it does not answer my question, the answer there just copy pastes from a Microsoft source. Why do I see different thread IDs in my test?

slugster
  • 49,403
  • 14
  • 95
  • 145
HAO
  • 89
  • 9
  • "It is obvious..." Why is it obvious? If the docs say no threads are created and the code reports that no threads are created, then what reason is there not to believe them? – ADyson Apr 08 '19 at 19:13
  • Because the thread id is different – HAO Apr 08 '19 at 19:14
  • Wow why close this thread? The output shows different thread IDs. So at least i would expect an explanation why that happens, instead of just closing the question – adjan Apr 08 '19 at 19:19
  • 2
    But I think that's because you're also using Tasks, which explicitly do create a new background thread. Async/await can be used without that. On their own, they don't cause new threads to be created. In as much as I understand it, they achieve the effect by taking advantage of the structures in multi core processors – ADyson Apr 08 '19 at 19:20
  • 1
    @Adrian Look at the second answer. You´ll get an explanation there. In other words: you won´t *need* multi-threading for async, but you *can*. In fact aync won´t ely on multiple threads at all, the scheduler will do so. – MakePeaceGreatAgain Apr 08 '19 at 19:21
  • 2
    @ADyson No, using tasks doesn't require using multiple threads at all. A task represents an asynchronous operation. Using another thread is one possible way of making something asynchronous. But not the only way. You can (and many people do) use tasks in an entirely single threaded application. – Servy Apr 08 '19 at 20:25
  • Thread behavior is specific to Task.Delay(). `await` has nothing whatever to do with it. Task.Delay() returns a Task, and that Task's completion routine (which is provided by code generated to handle the `async` keyword) is called from the timer's callback, which just so happens to be running on a different thread. – glenebob Apr 08 '19 at 22:18
  • 3
    The `await` does not produce the worker thread. That doesn't mean that no worker threads are created! Lots of things can create worker threads in an asynchronous workflow. `await` is not one of them. The question you should be asking is **can the continuation of an awaited task be scheduled onto a worker thread in a console application?** And the answer to that question is "yes". – Eric Lippert Apr 09 '19 at 00:35
  • 3
    If you are playing around with `async` for learning purposes, **do not do it from a console application**. Usually console applications are the easier environment to learn in, but that's not true for `async` because *there is no message loop to manage asynchrony in a console application*. Use a project type that has a top level message loop for processing asynchronous methods, like a winforms or wpf application. – Eric Lippert Apr 09 '19 at 00:36
  • 1
    You say that it is "obvious" that the delay is run on another thread, but since that is false, then you believe that something false is obviously true. **There is something wrong with how you are perceiving the world**. That's the real takeaway here: that you believe false things are "obviously true"; you will be more successful if you stop believing that false things are "obviously true" when they are neither obvious nor true. – Eric Lippert Apr 09 '19 at 16:59

2 Answers2

9

I always encourage people to read my async intro, and follow up with async best practices. In summary:

await by default captures a "context", and resumes executing the async method in that context. In this case, the context is the thread pool context. So, that's why you're seeing Test resume executing on a thread pool thread.

async and await by themselves do not create any additional threads; if you do the same in a UI app, for example, the "context" would be the UI thread, and Test would resume executing on that UI thread. But the context that is implicitly captured by await is what is responsible for scheduling the asnc continuation, and the context in this example just schedules it to the thread pool.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
1

You see different thread IDs in your test because different threads were used to execute your code. That does not mean the async/await caused the creation of additional threads. In this case, the threads used already existed in a pool of threads called the thread pool.

There are two distinct ideas:

  • tasks representing the work to be done, and
  • how the tasks are actually processed (e.g. scheduled and executed)

Async/await relate to the former but your test code is telling you something about the latter: the specific mechanism by which those tasks have been processed.

The mechanism is determined by the context. Some contexts use more than one thread (but note it is up to the context to decide how it manages those threads, including whether and when it creates new ones) but some only use 1 thread.

That more than one thread was used is to do with the context, not the async/await.

pere57
  • 652
  • 9
  • 18