简体   繁体   English

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

[英]Is it possible to kill WaitForSingleObject(handle, INFINITE)?

I am having problems closing an application that uses WaitForSingleObject() with an INFINITE timout. 我在关闭使用带有INTIMITE timout的WaitForSingleObject()的应用程序时遇到问题。

The full picture is this. 整个图片是这个。 I am doing the following to allow my application to handle the device wakeup event: 我正在执行以下操作以允许我的应用程序处理设备唤醒事件:

Register the event with: 通过以下方式注册活动:

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

Start a new thread to wait on: 启动一个新线程以等待:

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

Then do the following in the target method: 然后在目标方法中执行以下操作:

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

This all works fine until I try to close the application when, predictably, WaitForSingleObject continues to wait and does not allow the app to close properly. 一切正常,直到我尝试关闭应用程序为止,可以预见的是,WaitForSingleObject继续等待并且不允许应用程序正常关闭。 We only allow one instance of our app to run at a time and we check for this on startup. 我们一次只允许运行一个应用程序实例,我们会在启动时进行检查。 It appears to continue running until the device is soft reset. 它似乎继续运行,直到软复位设备为止。

Is there a way to kill the handle that WaitForSingleObject is waiting for, to force it to return? 有没有办法杀死WaitForSingleObject正在等待的句柄,以迫使它返回?

Many thanks. 非常感谢。

Use WaitForMultipleObject instead, and pass 2 handles. 改用WaitForMultipleObject,并传递2个句柄。 The existing one, and one for an event called something like 'exit'. 现有的一个,以及一个称为“ exit”之类的事件的事件。 During app shutdown, SetEvent on the exit event, and the WaitForMultipleObject will return and you can get it to exit the thread gracefully. 在应用程序关闭期间,退出事件将触发SetEvent,并且WaitForMultipleObject将返回,您可以让它正常退出线程。

You need to switch on the return value of WaitForMultipleObject to do the appropriate behaviour depending on which one of the handles was triggered. 您需要打开WaitForMultipleObject的返回值,以根据触发了哪个句柄来执行适当的行为。

Possibly, also, you can set the thread to be a background thread. 同样,也可以将线程设置为后台线程。 This will prevent it from stopping your application from shutting down when the main thread terminates. 这将防止它在主线程终止时阻止您的应用程序关闭。

See: 看到:

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

This is what I would do... 这就是我会做的...

  1. Use the EventWaitHandle class instead of calling CreateEvent directly. 使用EventWaitHandle类,而不是直接调用CreateEvent。 There shouldn't be any need to use the Windows API other than CeRunAppAtEvent (and API calls make code ugly...). 除了CeRunAppAtEvent之外,不需要使用Windows API(API调用使代码很难看...)。 Get this working first. 首先使此工作。
  2. Before creating the thread, create a ManualResetEvent variable that is not initially flagged. 在创建线程之前,创建一个最初未标记的ManualResetEvent变量。 Call it "TerminateEvent". 称之为“ TerminateEvent”。
  3. Replace the WaitForSingleObject API call with WaitHandle.WaitAny(WaitHandle[]) and pass an array containing "TerminateEvent" and the EventWaitHandle class wrapping the CeRunAppAtEvent notification. 用WaitHandle.WaitAny(WaitHandle [])替换WaitForSingleObject API调用,并传递一个包含“ TerminateEvent”的数组以及包装CeRunAppAtEvent通知的EventWaitHandle类。
  4. Your loop can use the return value of WaitAny to determine what to do. 您的循环可以使用WaitAny的返回值来确定要执行的操作。 The return value is the array index of the wait handle that unblocked the thread, so you can determine whether to continue the loop or not. 返回值是释放线程的等待句柄的数组索引,因此您可以确定是否继续循环。
  5. To cleanly end the thread, you can call "Set" on your "TerminateEvent" and then "Join" the thread to wait for it to terminate. 要干净地结束线程,可以在“ TerminateEvent”上调用“ Set”,然后“加入”线程以等待其终止。

'This all works fine until I try to close the application when, predictably, WaitForSingleObject continues to wait and does not allow the app to close properly.' “在我尝试关闭应用程序之前,一切正常,直到可以预期的是,WaitForSingleObject继续等待并且不允许应用程序正常关闭。”

Any app can close, no matter what its threads are doing. 任何应用程序都可以关闭,无论其线程在做什么。 If you call ExitProcess(0) from any thread in your app, the app will close, no matter if there are threads waiting INFINITE on some API/sychro, sleeping, running on another processor, whatever. 如果您从应用程序中的任何线程调用ExitProcess(0),则无论某个API / sychro上是否有等待INFINITE的线程,正在休眠,在其他处理器上运行的线程,该应用程序都会关闭。 The OS will change the state of all theads that are not running to 'never run again' and use its interprocessor driver to hard-interrupt any other processors that are actually running your thread code. 操作系统会将所有未运行的广告的状态更改为“不再运行”,并使用其处理器间驱动程序硬中断实际运行线程代码的所有其他处理器。 Once all the threads are stopped, the OS frees handles, segments etc and your app no longer exists. 一旦所有线程停止,操作系统将释放句柄,段等,并且您的应用程序将不再存在。

Problems arise when developers try to 'cleanly' shut down threads that are stuck - like yours, when the app is closing. 当开发人员试图“干净地”关闭被阻塞的线程时(例如您的应用程序关闭时),就会出现问题。 So.. 所以..

Do you have a TThread.WaitFor, or similar, in an OnClose/OnCloseQuery handler, FormDestroy or destructor? 在OnClose / OnCloseQuery处理程序,FormDestroy或析构函数中是否有TThread.WaitFor或类似的东西? If you have, and have no vital reason to ensure that the thread is terminated, just comment it out! 如果您有没有重要的理由要确保线程被终止,则将其注释掉!

This allows the main form to close and so your code will finally reach the ExitProcess() it has been trying to get at since you clicked on the red cross button 这将使主窗体关闭,因此您的代码将最终到达自单击红色十字按钮以来一直试图到达的ExitProcess()。

You could, of coure, just call ExitProcess() yourself, but this may leave you with resources leaked in other proceses - database connections, for example. 当然,您可以自己调用ExitProcess(),但这可能会使您陷入其他过程中的资源泄漏,例如数据库连接。

'216/217 errors on close if I don't stop the threads'. “如果我不停止线程,则关闭时会出现216/217错误”。 This often happens because developers have followed the er... 'unfortunate' Delphi thread examples and communicate with threads by directly exchanging data between secondary thread fields and main thread fields, (eg. TThread.synchronize). 这经常发生是因为开发人员遵循了错误的... Delphi线程示例,并通过在辅助线程字段和主线程字段之间直接交换数据(例如TThread.synchronize)与线程进行通信。 This just sucks and is hell-bent on causing problems, even in the app run, never mind at shutdown when a form has been destroyed and a thread is trying to write to it or a thread has been destroyed and a main-thread form is trying ot call methods on it. 即使在应用程序运行中,这也很糟糕,并且很容易引起问题,即使表单被破坏并且线程正在尝试向其写入数据或者线程被破坏并且主线程表单被破坏,也不必担心关机。尝试使用ot调用方法。 It is much safer to communicate asynchronously with threads by means of queueing/PostMessaging objects that outlive both of them, eg. 通过队列/ PostMessaging对象使两个线程都寿命更长,与线程异步通信要安全得多。 objects created in the thread/form and freed in the form/thread, or by means of a (thread-safe), pool of objects created in an initialization section. 在线程/表单中创建并在表单/线程中释放的对象,或通过(线程安全)在初始化部分中创建的对象池释放的对象。 Forms can then close/free safely while associated threads may continue to pointlessly fill up objects for handling until the main form closes, ExitProcess() is reached and the OS annihilates the threads. 然后,窗体可以安全地关闭/释放,而相关联的线程可能会继续无意义地填充对象以进行处理,直到关闭主窗体,到达ExitProcess()以及OS消灭线程为止。

'My Form handle is invalid because it has closed but my thread tries to post a message to it'. “我的表单句柄无效,因为它已关闭,但是我的线程尝试向其发布消息”。 If the PostMessage excepts, exit your thread. 如果PostMessage除外,请退出线程。 A better way is similar to the approach above - only post messages to a window that outlives all forms. 更好的方法与上述方法类似-仅将消息发布到比所有表单都有效的窗口中。 Create one in an initialization section with a trivial WndProc that only handles one const message number that all threads use for posting. 在一个普通的WndProc初始化部分中创建一个,该WndProc仅处理所有线程用于发布的一个const消息号。 You can use wParam to pass the TwinControl instance that the thread is trying to communicate with, (usually a form variable), while lParam passes the object being communicated. 您可以使用wParam传递线程尝试与之通信的TwinControl 实例 (通常是一个表单变量),而lParam则传递要通信的对象。 When it gets a message from a thread, WndProc calls 'Peform' on the TwinControl passed and the TwinControl will get the comms object in a message-handler. 当它从线程中获取消息时,WndProc在传递的TwinControl上调用“ Peform”,并且TwinControl将在消息处理程序中获取comms对象。 A simple global boolean, 'AppClosing', say, can stop the WndProc calling Peform() on TwinControls that are freeing themselves during shutdown. 例如,一个简单的全局布尔值“ AppClosing”可以停止WndProc在TwinControls上调用Peform(),该控件在关闭过程中正在释放自身。 This approach also avoids problems arising when the OS recreates your form window with a different handle - the Delphi form handle is not used and Windows will not recreate/change the handle of the simple form created in initialization. 当操作系统使用不同的句柄重新创建窗体窗口时,这种方法还可以避免出现问题-不使用Delphi窗体句柄,并且Windows不会重新创建/更改在初始化中创建的简单窗体的句柄。

I have followed these approaches for decades and do not get any shutdown problems, even with apps with dozens of threads slinging objects around on queues. 数十年来,我一直遵循这些方法,即使对于具有数十个线程的对象在队列中缠绕对象的应用程序,也没有遇到任何关机问题。

Rgds, Martin 马丁·罗格斯

Of course the preferable way to solve this is to use WaitForMultipleObjects , or any other suitable function that is able to wait for multiple criterias (such as WaitForMultipleObjects , MsgWaitForMultipleObjects , etc.). 当然,解决此问题的首选方法是使用WaitForMultipleObjects或能够等待多个条件的任何其他合适的函数(例如WaitForMultipleObjectsMsgWaitForMultipleObjects等)。

However if you have no control over which function is used - there're some tricky methods to solve this. 但是,如果您无法控制使用哪个函数-有一些棘手的方法可以解决此问题。 You may hack the functions imported from system DLL, by altering in memory the import table of any module. 您可以通过更改内存中任何模块的导入表来hack从系统DLL导入的功能。 Since WaitForMultipleObjects is exported from kernel32.dll - it's ok. 由于WaitForMultipleObjects是从kernel32.dll导出的,所以可以。 using this technics you may redirect the function caller into your hands, and there you will be able to use the WaitForMultipleObjects . 使用此技术,您可以函数调用程序重定向到您的手中,在那里您将可以使用WaitForMultipleObjects

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM