【问题标题】:Is it possible to kill WaitForSingleObject(handle, INFINITE)?是否可以杀死 WaitForSingleObject(handle, INFINITE)?
【发布时间】:2011-05-17 15:17:39
【问题描述】:

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

非常感谢。

【问题讨论】:

  • 线程不是我的强项,但不是用细长的锁或其他东西来终止这样的东西的想法(我不确定这具体是什么)你永远坚持下去,你可以用它来强制它在关闭期间结束?
  • 我试图中止线程(waitForWakeThread.Abort()),但我猜是因为它正在调用 WaitForSingleObject 它没有效果;没有抛出 ThreadAbortedException。

标签: c# multithreading winapi windows-ce waitforsingleobject


【解决方案1】:

改用 WaitForMultipleObject,并传递 2 个句柄。现有的一个,一个用于称为“退出”之类的事件。在应用关闭期间,退出事件上的SetEvent,WaitForMultipleObject将返回,您可以让它优雅地退出线程。

您需要打开 WaitForMultipleObject 的返回值,以根据触发的句柄之一执行适当的行为。

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

见:

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

【讨论】:

  • 谢谢,Scott,在关闭时与 WaitForMultipleObject 和 SetEvent 配合得很好。
【解决方案2】:

这就是我会做的......

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

【讨论】:

    【解决方案3】:

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

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

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

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

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

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

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

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

    几十年来,我一直遵循这些方法,没有遇到任何关闭问题,即使应用程序有数十个线程在队列中发送对象。

    Rgds, 马丁

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

      • WaitForMultipleObjects 在 Windows CE 的 kernel32.dll 中是 not - kernel32.dll 不存在。并且建议某人重定向基本 API 只是个坏建议。对应用程序/库的任何其他部分造成意外后果的风险很高,并且您使可维护性成为一场噩梦。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-04-09
      • 2017-04-02
      • 1970-01-01
      • 1970-01-01
      • 2021-10-01
      • 2011-09-10
      • 1970-01-01
      相关资源
      最近更新 更多