在 .NET / .NET Core 中,线程池的完成端口线程在异步 I/O 期间如何表现?
How do Completion Port Threads of the Thread Pool behave during async I/O in .NET / .NET Core?
.NET / .NET Core 线程池在内部使用两种不同类别的线程:工作线程和 I/O 完成端口 (IOCP) 线程。两者都是普通的托管线程,但用于不同的目的。通过不同的 API(例如 Task.Start
或 ThreadPool.QueueUserWorkItem
),我可以在工作线程上启动 CPU 绑定的异步操作(不应阻塞,否则线程池可能会创建额外的工作线程).
但是执行 I/O 绑定的异步操作呢? IOCP 线程在这些情况下的行为如何?具体来说,我有以下问题:
- 如果我启动异步 I/O 操作(例如,针对文件、管道或网络),我怀疑当前线程调度了异步请求。我还知道(通过书 "CLR via C#")CLR 注册到一个 I/O 完成端口,用于执行重叠异步 I/O。我怀疑这个IOCP绑定了async操作,以便稍后可以将async操作结果排队到线程池中。 因此,我的假设是否正确,即启动异步请求时没有触及 IOCP 线程?
- 我怀疑当 async I/O 操作的结果通过 CLR 的 I/O 完成端口报告时,这就是 IOCP 线程发挥作用的地方。结果排队到线程池,并使用 IOCP 线程来处理它。但是,在阅读一些论坛帖子时like this one on MSDN,我感觉IOCP线程实际上是用来发送请求然后阻塞直到返回结果。 是这样吗?当 I/O 操作由对方系统处理时,IOCP 线程是否阻塞?
async await
和 SynchronizationContext
呢? IOCP 线程是否处理异步 I/O 响应,然后例如在 UI 线程 上排队继续(假设未调用 ConfigureAwait(false)
)?
- Linux/MacOS X 上的 .NET Core 怎么样?没有 I/O 完成端口 - 它们是否以任何方式模拟?
Damien 和 Hans 在评论中为我指出了正确的方向,我想在这个答案中总结一下。
Damien 指出 Stephen Cleary's awesome blog post 回答了前三点:
- 异步 I/O 操作在调用线程上分派。不涉及 IOCP 线程。
- 因此,在异步期间没有 IOCP 线程阻塞 I/O。
- 当结果返回到 .NET 应用程序时,借用一个 IOCP 线程来标记任务完成。继续排队到目标
SynchronizationContext
或线程池。
Hans指出在Linux(epoll)和IOCP中有类似的机制
MacOS(kqueue)。
.NET / .NET Core 线程池在内部使用两种不同类别的线程:工作线程和 I/O 完成端口 (IOCP) 线程。两者都是普通的托管线程,但用于不同的目的。通过不同的 API(例如 Task.Start
或 ThreadPool.QueueUserWorkItem
),我可以在工作线程上启动 CPU 绑定的异步操作(不应阻塞,否则线程池可能会创建额外的工作线程).
但是执行 I/O 绑定的异步操作呢? IOCP 线程在这些情况下的行为如何?具体来说,我有以下问题:
- 如果我启动异步 I/O 操作(例如,针对文件、管道或网络),我怀疑当前线程调度了异步请求。我还知道(通过书 "CLR via C#")CLR 注册到一个 I/O 完成端口,用于执行重叠异步 I/O。我怀疑这个IOCP绑定了async操作,以便稍后可以将async操作结果排队到线程池中。 因此,我的假设是否正确,即启动异步请求时没有触及 IOCP 线程?
- 我怀疑当 async I/O 操作的结果通过 CLR 的 I/O 完成端口报告时,这就是 IOCP 线程发挥作用的地方。结果排队到线程池,并使用 IOCP 线程来处理它。但是,在阅读一些论坛帖子时like this one on MSDN,我感觉IOCP线程实际上是用来发送请求然后阻塞直到返回结果。 是这样吗?当 I/O 操作由对方系统处理时,IOCP 线程是否阻塞?
async await
和SynchronizationContext
呢? IOCP 线程是否处理异步 I/O 响应,然后例如在 UI 线程 上排队继续(假设未调用ConfigureAwait(false)
)?- Linux/MacOS X 上的 .NET Core 怎么样?没有 I/O 完成端口 - 它们是否以任何方式模拟?
Damien 和 Hans 在评论中为我指出了正确的方向,我想在这个答案中总结一下。
Damien 指出 Stephen Cleary's awesome blog post 回答了前三点:
- 异步 I/O 操作在调用线程上分派。不涉及 IOCP 线程。
- 因此,在异步期间没有 IOCP 线程阻塞 I/O。
- 当结果返回到 .NET 应用程序时,借用一个 IOCP 线程来标记任务完成。继续排队到目标
SynchronizationContext
或线程池。
Hans指出在Linux(epoll)和IOCP中有类似的机制 MacOS(kqueue)。