如何强制 GetQueuedCompletionStatus() 立即返回?

Posted

技术标签:

【中文标题】如何强制 GetQueuedCompletionStatus() 立即返回?【英文标题】:How to force GetQueuedCompletionStatus() to return immediately? 【发布时间】:2015-10-03 10:29:40 【问题描述】:

我有手工制作的线程池。线程从完成端口读取并做一些其他的事情。一个特定的线程必须结束。如果它挂在 GetQueuedCompletionStatus() 或 GetQueuedCompletionStatusEx() 上,如何中断它的等待?

有限超时 (100-1000 毫秒) 和退出变量远非优雅,会导致延迟并作为最后的手段。 目标线程中 APC 内的 CancelIo(completionPortHandle) 导致 ERROR_INVALID_HANDLE。 CancelSynchronousIo(completionPortHandle) 导致ERROR_NOT_FOUND。 带有终止​​包的 PostQueuedCompletionStatus() 不允许选择线程。 使用互斥锁的粗略 TerminateThread() 应该可以工作。 (我没有测试过。)但它在思想上是好的吗? 我试图等待特殊事件和完成端口。 WaitForMultipleObjects() 立即返回,好像完成端口已发出信号。 GetQueuedCompletionStatus() 节目没有返回任何内容。

我阅读了Overlapped I/O: How to wake a thread on a completion port event or a normal event? 并搜索了很多内容。

可能,问题本身——结束线程的工作——是糟糕设计的标志,我所有的线程应该是平等的,并混合到正常的线程池中。在这种情况下, PostQueuedCompletionStatus() 方法应该可以工作。 (虽然我怀疑这种方法是否美观且简洁,特别是如果线程使用 GetQueuedCompletionStatusEx() 一次获取多个数据包。)

【问题讨论】:

"Rough TerminateThread":在您终止时,您无法真正知道线程已挂起。因此它是不安全的。 为什么需要选择特定的线程? @usr 确实,即使我将数据包处理代码和终止调用包装在互斥体中,刚刚从队列中取出但未开始处理的数据包也可能会丢失。 @Ben “只是不要”就是我所说的“意识形态”。您的两个建议都有问题。 你不想取消IO,你只是想让线程退出,对吧?最好是在 APC 内设置一个标志。当GetQueuedCompletionStatusEx返回时,你必须检查返回码(因为如果有APC可能没有任何工作)然后检查标志是否必须退出。 【参考方案1】:

如果您只是想减小线程池的大小,则退出哪个线程并不重要。

但是,如果由于某种原因您需要向特定线程发出信号,它需要退出,而不是允许任何线程退出,您可以使用此方法。

如果您使用GetQueuedCompletionStatusEx,您可以通过将TRUE 传递给fAlertable 来进行警报等待。然后,您可以使用QueueUserAPC 将 APC 排队到要退出的线程。

https://msdn.microsoft.com/en-us/library/windows/desktop/ms684954(v=vs.85).aspx

如果线程很忙,那么您仍然需要等待当前工作项完成。

当然不要调用 TerminateThread。

【讨论】:

啊,我忘了说事件。我会更新并回答。简而言之,WaitForMultipleObjects 立即返回,好像完成端口已发出信号,但 GetQueuedCompletionStatus() 显示没有返回任何内容。 我试图执行空 APC 并导致等待返回!我仍然无法判断它是对还是错,但它似乎没问题。我已经再次阅读了好几次声明 fAlertable 的 TRUE 值来自GetQueuedCompletionStatusEx:当系统将 I/O 完成例程或 APC 排队到线程并且线程执行函数时,线程返回。谢谢。 是的,没错。一个警报等待将执行 APC 然后返回,无论 IO 完成是否可用。【参考方案2】:

不幸的是,I/O 完成端口句柄始终处于信号状态,因此不能真正用于WaitFor* 函数。

GetQueuedCompletionStatus[Ex] 是阻塞完成端口的唯一方法。对于空队列,只有当线程被警告时,该函数才会返回。正如@Ben 所提到的,QueueUserAPC 将使线程警报并导致GetQueuedCompletionStatus 返回。

但是,QueueUserAPC 分配内存,因此在内存不足或内存配额生效时可能会失败。 PostQueuedCompletionStatus 也是如此。因此,在退出路径上使用这些函数中的任何一个都不是一个好主意。

不幸的是,唯一可靠的方法似乎是调用由ntdll.dll 导出的未记录的NtAlertThread

extern "C" NTSTATUS __stdcall NtAlertThread(HANDLE hThread);

ntdll.lib 链接。这个函数会在不排队任何东西的情况下将目标线程置于警报状态。

【讨论】:

以上是关于如何强制 GetQueuedCompletionStatus() 立即返回?的主要内容,如果未能解决你的问题,请参考以下文章

强制自己

如何在父组件中强制刷新子组件

css 如何控制span 强制换行

如何让安卓手机强制4G网络模式

如何强制系统关闭底层套接字

当数据库强制加入时,如何强制执行更好的执行计划?