Consider a snippet of code such as this:
public async Task<Bitmap> DownloadDataAndRenderImageAsync(
CancellationToken cancellationToken)
{
var imageData = await DownloadImageDataAsync(cancellationToken);
return await RenderAsync(imageData, cancellationToken);
}
The first of the steps in this method is I/O bound work where as the second, computational.
When we rely on the compiler to generate the right task-based code for this asynchronous operation, what does the compiler do?
Specifically, does it know that the first one is I/O bound so it must use the TaskCompletionSource<T>
class so that there is no affinity between a thread and the task, and that for the second one, it can use any of the methods such as Run
or StartNew
or Start
to schedule the task on a thread-pool thread?
No. In the example you've given, the compiler will only use TaskCompletionSource<T>
(indirectly) for the overall asynchronous operation (DownloadDataAndRenderImageAsync
). It's up to the two methods that are called to decide how they're going to return the relevant task.
Maybe DownloadImageDataAsync
is itself an async
method which delegates down to some more async I/O. Maybe RenderAsync
calls Task.Run
. Those are both implementation details that the compiler doesn't care about at all when compiling DownloadDataAndRenderImageAsync
.
When we rely on the compiler to generate the right task-based code for this asynchronous operation, what does the compiler do?
In the example you give the compiler knows that DownloadImageDataAsync and RenderAsync are methods that return awaitables. Awaitables are objects that can be (1) queried for completion, and (2) have a continuation signed up to their completion. The compiler generates code that detects whether the returned awaitables are completed, and if they are not, signs up the remainder of the method as the completion of the awaitable.
Specifically, does it know that the first one is I/O bound
Nope. It knows that it returns something awaitable.
so it must use the TaskCompletionSource class so that there is no affinity between a thread and the task
If you care about the details of the completion logic, you're responsible for ensuring that the context in which the await happens is the right context. If you don't care, you'll get an appropriate default context.
and that for the second one, it can use any of the methods such as Run or StartNew or Start to schedule the task on a thread-pool thread?
The compiler doesn't do any such thing. The compiler generates code that checks whether the returned awaitable is complete, and if not, signs up the completion of the awaitable. How that awaitable works is the responsibility of the callee, not the compiler!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With