如何强制 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 排队到要退出的线程。
如果线程很忙,那么您仍然需要等待当前工作项完成。
当然不要调用 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() 立即返回?的主要内容,如果未能解决你的问题,请参考以下文章