重叠 I/O:如何在完成端口事件或正常事件上唤醒线程?

Posted

技术标签:

【中文标题】重叠 I/O:如何在完成端口事件或正常事件上唤醒线程?【英文标题】:Overlapped I/O: How to wake a thread on a completion port event or a normal event? 【发布时间】:2009-06-07 06:46:42 【问题描述】:

我想使用线程池来启动/取消重叠的读取操作——分别使用ReadFile()CancelIo()——以及在读取操作完成时处理任何完成端口事件。

    任何线程都可以启动读取操作 任何线程都可以处理读取完成事件 只有启动读取的线程可以取消它(这是CancelIo() 限制)

我不确定如何实现这一点。通常调用GetQueuedCompletionStatus() 来等待完成端口事件,调用WaitForSingleObject() 来等待正常事件,但不清楚如何将两者混合使用。如果PostQueuedCompletionStatus() 让我指定要唤醒的特定线程,我会被设置。有什么想法吗?

更新:该解决方案必须在 Windows XP 上运行。不幸的是,这排除了使用CancelIoEx()GetQueuedCompletionStatusEx()

【问题讨论】:

【参考方案1】:

1 和 2 很简单,只需使用 IO Completion 端口即可。

但是,正如您发现的那样,3 需要(在 Windows V61 之前)相同的线程。

如果使用 Windows >= V6,GetQueuedCompletionStatusEx 包含一个可更改选项,如果在线程上执行 APC,它将导致它返回。因此,当您需要特定线程执行其他工作时,请使用 QueueUserAPC 将无操作 APC1 排队。你当然需要一些线程安全队列来为被中断的线程提供取消的指令。

如果需要早期版本的兼容性,那么事情就会变得更加困难。可能性:

使用GetQueuedCompletionStatus](http://msdn.microsoft.com/library/aa364986)的超时参数定期返回检查是否取消。

或者,可能更实际地,将线程池分为两组。启动和取消 IO 的线程。这些线程的其余时间都在等待收到信号以执行这些操作之一。池的另一部分使用 GetQueuedCompletionStatus 等待 IO 完成。

这些都不是很好,但这始终是旧版本的问题:它们缺乏功能。

1 使用无操作 APC 而不是在 APC 中工作,这样可以绕过 APC 中可以完成的操作的限制及其与并发相关的固有问题。 (由于APC是在线程上执行的,线程持有的任何锁都保存在APC中,任何受保护的状态都会任意不一致。)

【讨论】:

不幸的是,GetQueuedCompletionStatusEx() 和 CancelIoEx() 都需要 Windows Vista。我将更新问题以说明我正在寻找向后兼容 Windows XP 的解决方案。 糟糕,忘了 GQCSEx 也在 >=Vista...更新中。 如何通知“启动和取消”线程池它应该调用 ReadFile()?如果我使用 WaitOnMultipleObjects() ,其中单个事件表示挂起的 ReadFile() 不是我将所有读取都瓶颈到单个线程吗? @Gill:我会让每个初始化/取消线程等待两个事件。 “此处的指令”和“正在关闭”,指令事件将表明该线程命令队列上有一条消息等待。该命令队列被其他线程(即完成池)用于发送指令(启动或取消)。它不需要单个初始化/取消线程(尽管您需要执行大量异步操作才能需要多个),但还需要创建多个线程的负载平衡(初始化指令可以由任何人完成,但取消需要具体)。 很好的答案,谢谢!作为旁注,我刚刚发现我不需要使用完成端口,因为我的 I/O 处理程序每​​个读/写请求只花费 0.3 毫秒(这不是错字)。我将通过使用单个线程来保持简单:)

以上是关于重叠 I/O:如何在完成端口事件或正常事件上唤醒线程?的主要内容,如果未能解决你的问题,请参考以下文章

Overlapped Model

Knightlab时间线js - 如何显示事件而不相互重叠

windows重叠I/O模型

Linux 和 I/O 完成端口?

LinuxI/O多路复用

LinuxI/O多路复用