是否有可能杀死WaitForSingleObject(handle,INFINITE)

本文关键字:handle INFINITE WaitForSingleObject 有可能 是否 | 更新日期: 2023-09-27 17:56:48

我在关闭使用无限超时的 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 正在等待的句柄,强制它返回?

非常感谢。

是否有可能杀死WaitForSingleObject(handle,INFINITE)

改用WaitForMultipleObjects,并传递 2 个句柄。现有的一个,一个用于称为"退出"之类的事件。在应用关闭期间,SetEvent退出事件,WaitForMultipleObjects将返回,你可以让它正常退出线程。

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

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

看:

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

这就是我会做的...

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

"这一切都很好,直到我尝试关闭应用程序,可以预见的是,WaitForSingleObject 继续等待并且不允许应用程序正确关闭。

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

当开发人员试图"干净"地关闭卡住的线程时,就会出现问题 - 就像你的线程一样,当应用程序关闭时。 所以。。

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

这允许主窗体关闭,因此您的代码最终将到达自您单击红叉按钮以来一直试图访问的 ExitProcess()

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

"如果我不停止线程,关闭时会出现 216/217 错误"。 这经常发生,因为开发人员遵循了错误..."不幸的"德尔菲线程示例,并通过直接在辅助线程字段和主线程字段之间交换数据来与线程通信,(例如。这很糟糕,并且一心想引起问题,即使在应用程序运行中也是如此,更不用说在关闭时,当表单被销毁并且线程正在尝试写入它或线程已被破坏并且主线程表单正在尝试调用方法时。 例如,通过队列/PostMessaging 对象与线程进行异步通信要安全得多,这些对象比它们都长。 在线程/窗体中创建并在窗体/线程中释放的对象,或通过在初始化部分中创建的对象池(线程安全)释放的对象。 然后,表单可以安全地关闭/释放,而关联的线程可能会继续毫无意义地填充对象进行处理,直到主表单关闭,到达 ExitProcess() 并且操作系统消灭线程。

"我的表单句柄无效,因为它已关闭,但我的线程尝试向其发布消息"。 如果帖子消息除外,请退出您的线程。 更好的方法类似于上述方法 - 仅将消息发布到比所有表单都长寿的窗口。使用简单的 WndProc 在初始化部分中创建一个,该 WndProc 仅处理所有线程用于发布的一个 const 消息编号。您可以使用 wParam 传递线程尝试与之通信的 TwinControl 实例(通常是表单变量),而 lParam 传递正在通信的对象。 当它从线程获取消息时,WndProc 在传递的 TwinControl 上调用"Peform",TwinControl 将在消息处理程序中获取通信对象。 例如,一个简单的全局布尔值"AppClosesing"可以阻止WndProc调用TwinControls上的Peform(),这些控件在关闭期间释放自己。 此方法还可以避免操作系统使用不同句柄重新创建表单窗口时出现的问题 - 不使用 Delphi 表单句柄,Windows 不会重新创建/更改在初始化中创建的简单表单的句柄。

几十年来,我一直遵循这些方法,并且没有遇到任何关机问题,即使具有数十个线程的应用程序在队列中抛出对象也是如此。

Rgds,Martin

当然,解决这个问题的最好方法是使用 WaitForMultipleObjects 或任何其他能够等待多个条件(如 WaitForMultipleObjectsMsgWaitForMultipleObjects 等)的合适函数。

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