Task.Delay 是否像 I/O 操作那样真正异步,即它是否依赖于硬件和中断而不是线程?
Is Task.Delay truly asynchronous like an I/O operation is, i.e. does it rely on hardware and interrupts instead of a thread?
我发现了一大堆相关的内容,但我一直找不到答案。我几乎 100% 确定 Task.Delay(int)
没有使用线程,因为我可以 运行 我的机器上只有 16 个逻辑处理器的代码:
var tasks = new List<Task>();
for(int i = 1; i < 100000; i++) tasks.Add(Task.Delay(10000));
await Task.WhenAll(tasks);
并且需要十秒才能完成。我认为,如果它使用大约十万个线程,则需要更长的时间。
所以我的问题是 Task.Delay(int)
是如何工作的? 不是 表示的方式,而是从线程和硬件资源的角度来看。
在 .NET 的当前实现中,只有一个“计时器线程”,它只跟踪托管计时器实例并在适当的时间引发它们的事件。此计时器线程将 block 其控制信号的超时设置为下一个计时器的到期时间。控制信号用于 add/remove/change 个计时器,因此当此阻塞请求超时时,计时器线程知道下一个计时器已触发。这是一个正常的线程阻塞操作,因此在内部,线程处于空闲状态并从调度程序队列中删除,直到该阻塞操作完成或超时。这些操作的超时由 OS 调度程序的定时器中断处理。
所以技术上有一个线程,但每个进程只有一个线程,而不是每个 Task.Delay
.
我再次强调,这是在 .NET 的 当前实现 中。已经提出了其他解决方案,例如每个 CPU 一个计时器线程,或者一个动态计时器线程池。也许他们因为某种原因被试验并被拒绝,或者将来可能会采用替代解决方案。据我所知,这在任何地方都没有正式记录,所以这是一个实现细节。
我发现了一大堆相关的内容,但我一直找不到答案。我几乎 100% 确定 Task.Delay(int)
没有使用线程,因为我可以 运行 我的机器上只有 16 个逻辑处理器的代码:
var tasks = new List<Task>();
for(int i = 1; i < 100000; i++) tasks.Add(Task.Delay(10000));
await Task.WhenAll(tasks);
并且需要十秒才能完成。我认为,如果它使用大约十万个线程,则需要更长的时间。
所以我的问题是 Task.Delay(int)
是如何工作的? 不是
在 .NET 的当前实现中,只有一个“计时器线程”,它只跟踪托管计时器实例并在适当的时间引发它们的事件。此计时器线程将 block 其控制信号的超时设置为下一个计时器的到期时间。控制信号用于 add/remove/change 个计时器,因此当此阻塞请求超时时,计时器线程知道下一个计时器已触发。这是一个正常的线程阻塞操作,因此在内部,线程处于空闲状态并从调度程序队列中删除,直到该阻塞操作完成或超时。这些操作的超时由 OS 调度程序的定时器中断处理。
所以技术上有一个线程,但每个进程只有一个线程,而不是每个 Task.Delay
.
我再次强调,这是在 .NET 的 当前实现 中。已经提出了其他解决方案,例如每个 CPU 一个计时器线程,或者一个动态计时器线程池。也许他们因为某种原因被试验并被拒绝,或者将来可能会采用替代解决方案。据我所知,这在任何地方都没有正式记录,所以这是一个实现细节。