I've heard a lot about how a new thread is not created with async await. I decided to check what would happen if there was an while(true) in the main function and the asynchronous function.
namespace asyncTest
{
public class Program
{
public static void Task1()
{
while (true) {
Console.WriteLine("Task1: " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000);
}
}
public static async void async()
{
Console.WriteLine("async start: " + Thread.CurrentThread.ManagedThreadId);
await Task.Run(Task1);
Console.WriteLine("async end: " + Thread.CurrentThread.ManagedThreadId);
}
static void Main(string[] args)
{
async();
while (true) {
Console.WriteLine("main: " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000);
}
}
}
}
But for some reason my functions are executed in different threads:
I want to understand why they are executed in different threads if async await does not create a thread. And I want to know how high-load operations are performed asynchronously, as in this example. Who performs the asynchronous operation on an additional thread? Or are they executed in the same thread?
Best Answer
To quote the classic There Is No Thread by Stephen Cleary (highly recommend to read it, emphasis is mine):
This does not mean that there are no threads involved at all though.
First of all you need to know that there are two "main" async scenarios: IO-bound and CPU-bound. From the Asynchronous programming scenarios:
Your example basically simulates a CPU-bound scenario via endless cycle with
Thread.Sleep
in theTask1
. It requires a thread to execute sinceThread.Sleep
is not an async operation:Another case when thread is needed - to complete the continuation (i.e. what is written after
await
), but the thread might or might not be created here - in this case this is managed by theThreadPool
(see also the The managed thread pool doc):which manages the threads and will reuse them if there are available ones.
Note that in this case no extra thread will be created to perform the "waitng" on per
await
used basis.As for IO-bound operations you can simulate an async operation with
Task.Delay()
for example. Consider the following modification to your code:depending on several factors you might see less unique threads in the output than you have simultaneously "running" async operations.
Notes:
There are a lot o nuances here. For example:
For IO-bound operation - it should be implemented correctly and that underlying system/device/driver supports it. For example Oracle driver for quite a long time didn't - see the Can the Oracle managed driver use async/await properly?
Execution of continuation is more complicated too - it involves several factors, including presence of the
SynchronizationContext
. See also answers to What thread runs the code after the `await` keyword?async void
Try avoiding this, in majority of cases it should be
async Task
(with exception of event handlers which usually are used in desktop/mobile apps)See also: