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

时间:2011-05-17 15:17:39

标签: c# multithreading winapi windows-ce waitforsingleobject

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

非常感谢。

4 个答案:

答案 0 :(得分:18)

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

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

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

请参阅:

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

答案 1 :(得分:1)

这就是我要做的......

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

答案 2 :(得分:1)

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

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

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

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

这允许主窗体关闭,因此您的代码最终会到达自单击红叉按钮后它一直试图进入的ExitProcess()

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

如果我不停止线程,

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

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

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

RGDS, 马丁

答案 3 :(得分:0)

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

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