Reputation: 2107
I'm curious about async await threading internals.
Everyone states that async is so much better in case of performance, because it frees threads that are waiting for a response to a long asynchronous call. Ok I get it.
But let's consider this scenario.
I have an async methodA executing an async operation on database. The api of the database exposes function BeginQuery and event QueryCompleted. I wrapped those with a task (with use of TaskCompletionSource).
My question is what is going under the hood between calling BeginQuery and firing event QueryCompleted.
I mean - doesn't it need to spawn some kind of worker to fire the event? At the very low level it must be some synchronous loop that is blocking a thread reading result from db.
What I suppose is that any async call must generate a thread to actually handle the response (maybe wait for it in a low level c++ loop in driver code).
So our only "gain" is that the caller thread can be freed when some other thread is doing its work.
Does calling an asynchronous method always create a new worker thread?
Could someone confirm my understanding?
Upvotes: 24
Views: 6230
Reputation: 11228
Async methods don't create new threads, they are based on Task
s, which may or may not use threads (as with TaskCompletionSource
), and even when they do there's no overhead because they use ThreadPool
.
Upvotes: 1
Reputation: 61656
I mean - doesn't it need to spawn some kind of worker to fire the event? At the very low level it must be some synchronous loop that is blocking a thread reading result from db.
Even when you actually have to wait for a kernel object (like a manual reset event), you still can turn a blocking synchronous code into asynchronous one and free the thread from blocking (updated: a real-life scenario).
For example, synchronous code:
void Consume()
{
var completedMre = new ManualResetEvent(false);
producer.StartOperation(completedMre);
completedMre.WaitOne(); // blocking wait
Console.WriteLine(producer.DequeueResult());
}
Asynchronous analog:
async Task ConsumeAsync()
{
var completedMre = new ManualResetEvent(false);
producer.StartOperation(completedMre);
var tcs = new TaskCompletionSource<Result>();
ThreadPool.RegisterWaitForSingleObject(completedMre,
(s, t) => tcs.SetResult(producer.DequeueResult()),
null, Timeout.Infinite, true);
var result = await tcs.Task;
Console.WriteLine(result);
}
The asynchronous version scales for up to 64 times better (MAXIMUM_WAIT_OBJECTS
, that's the maximum number of kernel objects which can be aggregated by RegisterWaitForSingleObject
for waiting on a single thread). So, you can call Consume()
for 64 times in parallel and it will block 64 threads. Or you can call ConsumeAsync
for 64 times and it will block just one thread.
Upvotes: 5
Reputation: 35885
I mean - doesn't it need to spawn some kind of worker to fire the event? At the very low level it must be some synchronous loop that is blocking a thread reading result from db.
It will create an IO completion port (IOCP) representing a task that is being processed outside, and the thread will continue with other things. Then, when the IOCP notifies that the task is done, a thread will pick up the state of the IOCP and will continue the task.
http://www.drdobbs.com/cpp/multithreaded-asynchronous-io-io-comple/201202921
I/O completion ports provide an elegant solution to the problem of writing scalable server applications that use multithreading and asynchronous I/O.
Upvotes: 6
Reputation: 456322
Everyone states that async is so much better in case of performance, because it frees threads that are waiting for a response to a long asynchronous call.
Yes and no. The point behind async
is to free up the calling thread. In UI applications, the primary benefit of async
is responsiveness, because the UI thread is freed up. In server applications, the primary benefit of async
is scalability, because the request thread is freed up to handle other requests.
So our only "gain" is that the caller thread can be freed when some other thread is doing its work. Does always calling an asynchronous method is creating a new worker thread?
No. At the OS level, all I/O is asynchronous. It is the synchronous APIs which block a thread while the underlying asynchronous I/O is in progress. I recently wrote this up in a blog post: there is no thread.
Upvotes: 40