是否有可能杀死WaitForSingleObject(句柄,INFINITE)?

我在关闭使用带有INFINITE组合的WaitForSingleObject()的应用程序时遇到问题。

全貌是这样的。 我正在执行以下操作以允许我的应用程序处理设备唤醒事件:

注册活动:

CeRunAppAtEvent("\\\\.\\Notifications\\NamedEvents\\WakeupEvent", NOTIFICATION_EVENT_WAKEUP); 

启动一个新线程等待:

 Thread waitForWakeThread = new Thread(new ThreadStart(WaitForWakeup)); waitForWakeThread.Start(); 

然后在目标方法中执行以下操作:

 private void WaitForWakeup() { IntPtr handle = CreateEvent(IntPtr.Zero, 0, 0, "WakeupEvent"); while (true) { WaitForSingleObject(handle, INFINITE); MessageBox.Show("Wakey wakey"); } } 

这一切都正常,直到我尝试关闭应用程序时,可预见的是,WaitForSingleObject继续等待并且不允许应用程序正常关闭。 我们只允许我们的应用程序的一个实例一次运行,我们在启动时检查这个。 它似乎继续运行,直到设备软复位。

有没有办法杀死WaitForSingleObject正在等待的句柄,强制它返回?

非常感谢。

改为使用WaitForMultipleObject,并传递2个句柄。 现有的一个,一个叫做“退出”的事件。 在app关闭期间,退出事件上的SetEvent和WaitForMultipleObject将返回,您可以让它正常退出线程。

您需要打开WaitForMultipleObject的返回值以执行相应的行为,具体取决于触发了哪个句柄。

也可以将线程设置为后台线程。 这将阻止它在主线程终止时停止应用程序关闭。

看到:

http://msdn.microsoft.com/en-us/library/system.threading.thread.isbackground.aspx

这就是我要做的……

  1. 使用EventWaitHandle类而不是直接调用CreateEvent。 除了CeRunAppAtEvent之外,不应该使用Windows API(并且API调用使代码变得丑陋……)。 让这个工作第一。
  2. 在创建线程之前,创建一个最初未标记的ManualResetEvent变量。 称之为“TerminateEvent”。
  3. 用WaitHandle.WaitAny(WaitHandle [])替换WaitForSingleObject API调用,并传递一个包含“TerminateEvent”的数组和包含CeRunAppAtEvent通知的EventWaitHandle类。
  4. 您的循环可以使用WaitAny的返回值来确定要执行的操作。 返回值是取消阻塞线程的等待句柄的数组索引,因此您可以确定是否继续循环。
  5. 要干净地结束线程,可以在“TerminateEvent”上调用“Set”,然后“加入”线程以等待它终止。

“这一切正常,直到我尝试关闭应用程序时,可预见的是,WaitForSingleObject继续等待并且不允许应用程序正常关闭。”

任何应用都可以关闭,无论它的线程在做什么。 如果从应用程序中的任何线程调用ExitProcess(0),应用程序将关闭,无论是否有线程在某些API / sychro上等待INFINITE,hibernate,在另一个处理器上运行,无论如何。 操作系统会将未运行的所有磁盘的状态更改为“永不再次运行”,并使用其处理器间驱动程序硬中断任何其他实际运行线程代码的处理器。 一旦所有线程都停止,操作系统将释放句柄,段等,并且您的应用程序不再存在。

当应用程序关闭时,开发人员试图“干净地”关闭被卡住的线程(就像你的那样)会出现问题。 所以..

在OnClose / OnCloseQuery处理程序,FormDestroy或析构函数中是否有TThread.WaitFor或类似的东西? 如果您有,并且没有重要的理由确保线程被终止,请将其注释掉!

这允许主窗体关闭,因此您的代码将最终到达它已经尝试进入的ExitProcess(),因为您单击了红色十字按钮

您可以自己直接调用ExitProcess(),但这可能会让您在其他过程中泄露资源 – 例如数据库连接。

‘如果我不停止线程,则关闭216/217错误’。 这种情况经常发生,因为开发人员已经遵循了“不幸的”Delphi线程示例,并通过直接在辅助线程字段和主线程字段之间交换数据来与线程进行通信(例如,TThread.synchronize)。 即使在应用程序运行中,这只是糟透了,并且一心想要引起问题,当表单被销毁并且线程正在尝试写入或者线程已被销毁且主线程forms为尝试ot调用方法。 通过排队/ PostMessaging对象与线程异步通信更加安全,例如。 在线程/表单中创建并在表单/线程中释放的对象,或者通过在初始化部分中创建的(线程安全的)对象池释放的对象。 然后表单可以安全地关闭/释放,而关联的线程可能会继续毫无意义地填充对象以进行处理,直到主窗体关闭,到达ExitProcess()并且OS消灭线程。

‘我的表单句柄无效,因为它已关闭但我的线程试图向其发送消息’。 如果PostMessage除外,退出您的主题。 更好的方法类似于上面的方法 – 仅将消息发布到比所有表单更长的窗口。 在初始化部分创建一个简单的WndProc,它只处理一个所有线程用于发布的const消息号。 您可以使用wParam传递线程尝试与之通信的TwinControl 实例 (通常是表单变量),而lParam传递正在传递的对象。 当它从一个线程获得一条消息时,WndProc在TwinControl上调用’Peform’并且TwinControl将在消息处理程序中获取comms对象。 例如,一个简单的全局布尔值’AppClosing’可以阻止WndProc在TwinControls上调用Peform(),它们在关闭期间自行释放。 这种方法还避免了操作系统使用不同的句柄重新创建窗体窗口时出现的问题 – 不使用Delphi表单句柄,Windows不会重新创建/更改初始化中创建的简单表单的句柄。

几十年来我一直遵循这些方法,并且不会出现任何关闭问题,即使应用程序中有数十个线程在队列中投放对象。

Rgds,马丁

当然,解决此问题的最佳方法是使用WaitForMultipleObjects ,或任何其他能够等待多个标准的合适函数(例如WaitForMultipleObjectsMsgWaitForMultipleObjects等)。

但是,如果你无法控制使用哪个函数 – 有一些棘手的方法可以解决这个问题。 您可以通过在内存中更改任何模块的导入表来hack从系统DLL导入的函数。 由于WaitForMultipleObjects是从kernel32.dll导出的 – 没关系。 使用此技术,您可以函数调用者重定向到您的手中,然后您就可以使用WaitForMultipleObjects