C#编译器可以区分I/O绑定和计算任务吗?

Wat*_* v2 25 .net c# asynchronous async-await

考虑一段代码,例如:

public async Task<Bitmap> DownloadDataAndRenderImageAsync(
    CancellationToken cancellationToken)
{
    var imageData = await DownloadImageDataAsync(cancellationToken);
    return await RenderAsync(imageData, cancellationToken);
}
Run Code Online (Sandbox Code Playgroud)

此方法中的第一步是I/O绑定工作,其中第二步是计算.

当我们依赖编译器为这个异步操作生成正确的基于任务的代码时,编译器会做什么?

具体来说,它是否知道第一个是I/O绑定所以它必须使用TaskCompletionSource<T>该类,以便线程和任务之间没有亲和力,而对于第二个,它可以使用任何方法,如RunStartNew或者Start在线程池线程上安排任务?

Jon*_*eet 40

在您给出的示例中,编译器将仅TaskCompletionSource<T>(间接)使用整个异步操作(DownloadDataAndRenderImageAsync).由两个方法决定它们将如何返回相关任务.

也许DownloadImageDataAsync它本身就是一种async委托更多异步I/O的方法.也许是RenderAsync电话Task.Run.这些都是实现细节,编译器不关心在所有编译时DownloadDataAndRenderImageAsync.


Eri*_*ert 34

当我们依赖编译器为这个异步操作生成正确的基于任务的代码时,编译器会做什么?

在示例中,您给编译器知道DownloadImageDataAsync和RenderAsync是返回awaitables的方法.等待是可以(1)查询完成的对象,以及(2)在完成之前签署的继续.编译器生成的代码检测返回的等待是否已完成,如果不是,则在等待的完成时注册方法的其余部分.

具体来说,它是否知道第一个是I/O绑定

不.它知道它返回了一些等待的东西.

所以它必须使用TaskCompletionSource类,以便线程和任务之间没有亲和力

如果您关心完成逻辑的细节,那么您有责任确保await发生的上下文是正确的上下文.如果您不在乎,您将获得适当的默认上下文.

而对于第二个,它可以使用任何方法,如Run或StartNew或Start来在线程池线程上安排任务?

编译器没有做任何这样的事情.编译器生成的代码检查返回的等待是否完成,如果没有,则报告等待完成.等待的工作方式是被调用者的责任,而不是编译器的责任!


usr*_*usr 16

编译器和运行时都不知道.事实上,整个术语"IO界限"含糊不清.是否有任何操作系统调用IO?!正在睡觉或计时IO?!

如果你不得不提出这个问题,你可能会对任务产生一些误解,因为通常没有必要知道这一点.

也许认为等待开始任务是常见的错误?它等待现有任务完成.DownloadImageDataAsyncRenderAsync决定如何以及何时完成该任务.因此他们决定是使用CPU还是执行IO.

DownloadImageDataAsyncRenderAsync手你一个任务,你不知道里面有什么,你不需要知道.