【问题标题】:Windows: Under what circumstances might SetEvent() not return immediately?Windows:在什么情况下 SetEvent() 可能不会立即返回?
【发布时间】:2008-11-21 23:32:45
【问题描述】:

我有一个线程,当它的函数退出其循环(退出由事件触发)时,它会进行一些清理,然后设置一个不同的事件让主线程知道它已经完成。

但是,在某些情况下,SetEvent() 在设置线程的“我完成”事件后似乎不会返回。

此线程是 DLL 的一部分,并且在加载/附加 DLL、启动线程、结束线程以及多次分离/卸载 DLL 且应用程序未在其间关闭的情况下,该问题似乎发生了。在此问题发生之前必须重复此序列的次数是可变的。

如果您怀疑我知道我在说什么,我已经通过将 SetEvent() 调用与对 OutputDebugString() 的调用括起来来确定发生了什么。出现 SetEvent() 之前的输出。然后,等待线程产生指示事件已设置的输出。

但是,退出线程中对 OutputDebugString() 的第二次调用(在 SetEvent() 之后的那个)永远不会发生,或者至少它的字符串永远不会出现。如果发生这种情况,应用程序会在片刻后崩溃。

(请注意,对 OutputDebugString() 的调用是在问题开始发生后添加的,因此它不太可能挂在那里,而不是在 SetEvent() 中。)

我不完全确定导致崩溃的原因,但它发生在 SetEvent() 没有立即返回的同一个线程中(我一直在跟踪/输出线程 ID)。我想 SetEvent() 有可能最终返回,此时它返回的上下文已经消失/无效,但是什么会导致这样的延迟?

原来看这段代码这么久我都被蒙蔽了,甚至都没有想到去检查返回码。我今天已经看完了,所以我会在星期一知道它返回了什么(如果它返回了),然后我会用该信息编辑这个问题。

更新:我将(主)代码更改为等待线程退出而不是设置事件,并从从线程中删除了 SetEvent() 调用。这改变了 bug 的性质:现在,它不再从 SetEvent() 返回,而是根本不退出线程,整个事情都挂起。

这表明问题不在于 SetEvent(),而是更深层次的问题。还不知道是什么,但最好不要追着那条死胡同。

更新(09 年 2 月 13 日):
事实证明,当我问这个问题时,问题比我想象的要深。 jdigital(可能还有其他人)几乎解决了根本问题:我们试图在分离 DLL 的过程中卸载线程。

我当时没有意识到,但后来通过这里和其他地方的研究(例如 Raymond Chen 的博客)发现,这是一件非常糟糕的事情。

问题在于,由于它的编码方式和行为方式,这并不明显是潜在的问题 - 它被伪装成我必须克服的各种其他不良行为。

这里的一些建议帮助我做到了这一点,所以我感谢所有做出贡献的人。谢谢!

【问题讨论】:

    标签: windows


    【解决方案1】:

    谁在卸载 DLL,卸载是在什么时间完成的?我想知道这里是否存在时间问题,即在线程运行完成之前卸载 DLL。

    【讨论】:

      【解决方案2】:

      您是否取消引用 HANDLE * 以传递给 SetEvent?更有可能是事件句柄引用无效并且崩溃是访问冲突(即访问垃圾)。

      【讨论】:

        【解决方案3】:

        您可能希望使用 WinDbg 捕获崩溃并检查堆栈。

        【讨论】:

          【解决方案4】:

          为什么需要在slave线程中设置一个事件来触发master线程线程完成?只需要退出线程,调用主线程应该等待工作线程退出,示例伪代码-

          Master
          {
             TerminateEvent = CreateEvent ( ... ) ;
             ThreadHandle = BeginThread ( Slave, (LPVOID) TerminateEvent ) ;
             ...
             Do some work
             ...
             SetEvent ( TerminateEvent ) ;
             WaitForSingleObject ( ThreadHandle, SOME_TIME_OUT ) ;
             CloseHandle ( TerminateEvent ) ;
             CloseHandle ( ThreadHandle ) ; 
          }
          
          Slave ( LPVOID ThreadParam )
          {
             TerminateEvent = (HANDLE) ThreadParam ;
             while ( WaitForSingleObject ( TerminateEvent, SOME__SHORT_TIME_OUT ) == WAIT_TIMEOUT )
             { 
                ... 
                Do some work 
                ...
             }
          }
          

          有很多错误条件和状态需要检查,但这是我通常这样做的本质。

          如果你能掌握它,就得到这本书,当我多年前第一次阅读它时,它改变了我在 Windows 开发方面的生活。

          Advanced Windows: The Developer's Guide to the Win32 Api for Windows Nt 3.5 and Windows 95 (Paperback), by Jeffrey Richter (Author)

          【讨论】:

            猜你喜欢
            • 2020-02-07
            • 2020-11-04
            • 2015-03-06
            • 1970-01-01
            • 2021-08-28
            • 1970-01-01
            • 1970-01-01
            • 2016-06-13
            • 1970-01-01
            相关资源
            最近更新 更多