Does the use of async/await create a new thread?
Solution 1
In short NO
From Asynchronous Programming with Async and Await : Threads
The async and await keywords don't cause additional threads to be created. Async methods don't require multithreading because an async method doesn't run on its own thread. The method runs on the current synchronization context and uses time on the thread only when the method is active. You can use Task.Run to move CPU-bound work to a background thread, but a background thread doesn't help with a process that's just waiting for results to become available.
Solution 2
According to MSDN : async keyword
An async method runs synchronously until it reaches its first await expression, at which point the method is suspended until the awaited task is complete. In the meantime, control returns to the caller of the method, as the example in the next section shows.
Here is a sample code to check it :
class Program
{
static void Main(string[] args)
{
Program p = new Program();
p.Run();
}
private void Print(string txt)
{
string dateStr = DateTime.Now.ToString("HH:mm:ss.fff");
Console.WriteLine($"{dateStr} Thread #{Thread.CurrentThread.ManagedThreadId}\t{txt}");
}
private void Run()
{
Print("Program Start");
Experiment().Wait();
Print("Program End. Press any key to quit");
Console.Read();
}
private async Task Experiment()
{
Print("Experiment code is synchronous before await");
await Task.Delay(500);
Print("Experiment code is asynchronous after first await");
}
}
We see the code of Experiment() method after await executes on another Thread.
But if I replace the Task.Delay by my own code (method SomethingElse) :
class Program
{
static void Main(string[] args)
{
Program p = new Program();
p.Run();
}
private void Print(string txt)
{
string dateStr = DateTime.Now.ToString("HH:mm:ss.fff");
Console.WriteLine($"{dateStr} Thread #{Thread.CurrentThread.ManagedThreadId}\t{txt}");
}
private void Run()
{
Print("Program Start");
Experiment().Wait();
Print("Program End. Press any key to quit");
Console.Read();
}
private async Task Experiment()
{
Print("Experiment code is synchronous before await");
await SomethingElse();
Print("Experiment code is asynchronous after first await");
}
private Task SomethingElse()
{
Print("Experiment code is asynchronous after first await");
Thread.Sleep(500);
return (Task.CompletedTask);
}
}
I notice the thread remains the same !
In conclusion, I'll say async/await code could use another thread, but only if the thread is created by another code, not by async/await.
In this case, I think Task.Delay
created the thread, so I can conclude async/await does not create a new Thread like said by @Adriaan Stander.
Solution 3
Sorry for being late to the party.
I am new to TPL and I am wondering: How does the asynchronous programming support that is new to C# 5.0 (via the new async and await keywords) relate to the creation of threads?
async/await
is not introduced for thread creation, but to utilize the current thread optimally.
Your app might read files, wait for response from another server or even do a computation with high memory access (Simply any IO task). These tasks are not CPU intensive (Any task that will not use 100% of your thread).
Think about the case when you are processing 1000 non CPU intensive tasks. In this case, process of creating 1000s of OS level thread might eat up more CPU and Memory than doing actual work on a single thread (4mb per thread in Windows, 4MB * 1000 = 4GB). At the same time if you run all the tasks sequentially, you might have to wait until the IO tasks gets finished. Which end up in long time to complete the task, while keeping the CPU idle.
Since we require parallelism to complete multiple tasks quickly, at the same time all parallel tasks are not CPU hungry, but creating threads is inefficient.
The compiler will break the execution at any method call to an async
method (which gets called with an await) and immediately execute the code outside of the current code branch, once an await
is reached, the execution will go inside the previous async
. This will be repeated again and again until all the async calls are completed and their awaiters
are satisfied.
If any of the async method have heavy CPU load without a call to an async method, then yes, your system will become unresponsive and all the remaining async methods will not get called until the current task is finished.
Solution 4
So I've been reading up on the threading model, and Async / Await can certainly lead to new threads being used (not necessarily created - the pool creates them at application start). It's up to the scheduler to determine if a new thread is needed. And as I see it, a call to an awaitable function may have internal details that increase the chances of the scheduler utilizing another thread; simply because more work means more opportunities / reasons for the scheduler to divvy out work.
WinRT async operations automatically happen on the thread pool. And typically you will be calling FROM the thread pool, except for UI thread work .. Xaml/Input/Events.
Async operations started on Xaml/UI threads have their results delivered back to the [calling] UI thread. But asynchronous operation results started from a thread pool thread are delivered wherever the completion happens, which may not be the same thread you were on before. The reason behind this is that code written for the thread pool is likely to be written to be thread safe and it is also for efficiency, Windows doesn't have to negotiate that thread switch.
So again, in answer to the OP, new threads are not necessarily created but your application can and will use multiple threads to complete asynchronous work.
I know this seems to contradict some of the literature regarding async / await, but that's because although the async / await construct is not by itself multithreaded. Awaitables are the, or one of the mechanisms by which the scheduler can divide work and construct calls across threads.
This is at the limit of my knowledge right now regarding async and threading, so I might not have it exactly right, but I do think it's important to see the relationship between awaitables and threading.
dev hedgehog
Updated on July 20, 2022Comments
-
dev hedgehog over 1 year
I am new to TPL and I am wondering: How does the asynchronous programming support that is new to C# 5.0 (via the new
async
andawait
keywords) relate to the creation of threads?Specifically, does the use of
async/await
create a new thread each time that they are used? And if there many nested methods that useasync/await
, is a new thread created for each of those methods? -
dudeNumber4 over 7 yearsNo matter how many times I look at this issue, I still don't understand. "Async methods don't require multithreading because an async method doesn't run on its own thread" Ergo -> another thread. How can it be otherwise?
-
Vakhtang over 7 yearsNot every operation needs a thread. There are a lot of processors/controllers on a typical system including disk controllers, network card controllers, GPUs etc. They just need to receive a command from a processor. They then proceed to execute the command and tell the processor when finished (notify it by interrupting it or some other mechanism). Until then, there is no thread involved. The issuing thread can either go to sleep or go to a thread pool where it can be reused. When the command is finished, program execution may be continued by the sleeping thread or a thread pool thread.
-
Admin over 7 yearswhat if async/await method is a cpu bound method Task.Run is used to process long running process in that case asycn with require the new thread, no?
-
Bart about 7 years@dudeNumber4 "An async method doesn't run on it's own thread -> ergo another thread". No it runs on the same thread! The same thread as the method that called it. It just returns to that calling method in case it starts 'await'ing something so as not to waste CPU cycles.
-
ajeh about 6 years@Bart CPU cycles are irrelevant in this q/a.
-
Enigmativity over 5 yearsWhy does the code sample create a new thread? What is specific about this code that makes it a good example to show?
-
Gavin Williams over 5 yearsGetFileAsync() creates threads and will return on the created thread, why it creates threads? Because that's the way Microsoft designed it I guess. So if you think you can use async/await as in this example and still be on the thread you were on when you called GetFileAsync() you would be wrong, and in thread sensitive situations such as DirectX device creation / rendering you're application will crash.
-
Scott Chamberlain over 5 years@GavinWilliams the thread that is used for the callback is chosen by
TaskScheduler.Current
at the time of callingawait
not the function you called await on. The fact that it runs on a different thread when it returns has nothing to do withGetFileAsync
-
Enigmativity over 5 years@GavinWilliams - There is nothing in the code that you've shown that makes this a good example without the reader having specific knowledge of the implementation of
GetFileAsync
. That makes this a poor example. -
Gavin Williams over 5 years@Enigmativity I don't get you sorry. I think typically a user of GetFileAsync or of any other method for that matter is not going to have specific knowledge of it's implementation. I gave it as an example of async / await usage resulting in threads being created. It does show that result. It's a very common async method and I would suggest is a typical example. Is there something atypical about it that makes it a poor example? In practice if you use async/await you have to be careful to manage which threads your code is on.
-
Voo over 5 yearsI'd be very surprised if something such as GetFileAsync always created new threads. Any proof for that claim? Particularly IO rarely had to create threads since completion ports are a thing. But then I don't know much about uwp and what the function does in detail
-
Gavin Williams over 5 yearsOnly empirical proof.
-
Gavin Williams over 5 yearsI always have to be careful when using async because it can throw sections of my asset creation code onto a different from my rendering thread and you can't use the DirectX Immediate context from different threads.
-
Enigmativity over 5 years@GavinWilliams - If I gave you the signature of a method, lets say
Task<int> GetAgeAsync()
, you can't tell by looking at it if this will or will not create a thread. You have to show the implementation. So your example is the same. There is no way to tell if this creates a thread or not. Had you presented your own example with the full implementation ofGetFileAsync
orGetAgeAsync
then you could show that it does or does not. -
Gavin Williams over 5 yearsThere is no way to tell - that's what I'm saying. You don't know if a thread will be created or not, in my answer I say .. "Using Async/Await doesn't necessarily cause a new thread to be created. But the use of Async/Await can lead to a new thread to be created because the awaitable function may internally spawn a new thread." That is true, although it seems that there is another mechanism by which a thread can be created, as suggested by Scott Chamberlain, and that is that the TaskSchedular can make it's own determination as to whether to spawn a thread.
-
Voo over 5 years@Gavin I don't see how
GetFileAsync(path)); // << new threads
andthe following code spawns new threads
is supposed to mean "I don't know whether this creates new threads or not". -
maoyang about 5 yearsI think it uses a similar mode with Nodejs. There are a thread pool and an event loop behind the idea.
-
Welcor almost 4 yearsI think this is wrong. you mix up pure
await DoAsync()
withawait Task.Run(Do)
. the latter will use the thread pool because of the Task.Run but not because of the await. I mean even the official documentation saysThe async and await keywords don't cause additional threads to be created.
. -
Gavin Williams almost 4 years@Blechdose Yeah, so as I understand it, if you're just using the async / await glue on a Task with synchronous code (single threaded), then that's fine, it will run on the calling thread. But for any awaitable methods that are black boxes. You don't know if another thread will be used. You should never make assumptions about which thread you're on after awaiting.
-
Elo over 3 yearsWhy do you say "The compiler will break the execution at any method call to an async method (regardless it is awaited or not)" ? Microsoft says : "If the method that the async keyword modifies doesn't contain an await expression or statement, the method executes synchronously". In this case, the async keyword doesn't break execution flow at all. Source : docs.microsoft.com/fr-fr/dotnet/csharp/language-reference/…
-
The Muffin Man over 3 yearsThe name async implies IO bound work such as if the work is to send an HTTP request to amazon you don't need to spawn a thread that does nothing, but wait. However, the async/await feature also allows you a cleaner interface for CPU bound work (that involves threads) that would normally involve messy callback syntax. As far as I understand, the final piece is
ConfigureAwait(false)
which specifies whether the work needs to absolutely be synced back to the thread that started it (which I believe in most cases is no) which isn't as performant. -
VibeeshanRC about 3 yearsThe wording "(regardless it is awaited or not)" is wrong. fixed now. thanks for the correction.
-
Bartosz about 3 yearsOne thing that puzzles me is that here docs.microsoft.com/en-us/dotnet/api/… it is said
work performed by a Task object typically executes asynchronously on a thread pool thread rather than synchronously on the main application thread
, which to me sort of means that there is a thread assigned. Although I read Stephen Cleary's blog post and am convinced there is no thread... :) blog.stephencleary.com/2013/11/there-is-no-thread.htm -
Timeless over 2 yearsthis explanation is pretty good. stackoverflow.com/a/59918139/926460
-
Denny Jacob over 2 yearsA thread has gaps in its processing timeline when it is waiting on devices like GPU, disk controller etc. Awaited tasks use these gaps in a non blocking way. The gaps are so many that your task appears to have completed concurrently.
-
DaniDev over 2 yearsThanks for this. I think it clarifies some of the confusion which arises from the fact that Async Await is often used with a Task in TAP (Task Async Pattern) . As such some people mistakenly assume that the Async Await create a new thread rather then Task. As I understand it the TAP pattern provides a way to manage multi threading functionality more cleanly.