【问题标题】:WaitForSingleObject( )等待单个对象()
【发布时间】:2009-06-08 12:39:08
【问题描述】:

我在这里陷入了一个非常惊人的问题。代码如下所示。

class A
{
   public:  
   A(){  
         m_event =  CreateEvent(NULL, false, false, NULL);       // create an event with initial value as non-signalled
         m_thread = _beginthread(StaticThreadEntry, 0, this);    // create a thread 
      }

   static void StaticThreadEntry(A *  obj) {  obj->ThreadEntry();  }

   void ThreadEntry();
};

void A::ThreadEntry()
{
     WaitforSingleObject(m_event,INFINITE);
}


int main()
{
        A a;

        SetEvent(m_event);     // sets the event to signalled state which causes the running thread to terminate 

        WaitForSingleObject(m_thread, INFINITE);  // waits for the thread to terminate

        return 0;
 } 

问题:

运行上述代码时,有时(5 次中有 1 次)它会挂起,并且控件会卡在对 WaitforSingleObject() 的调用中(在主函数内部)。代码始终调用 SetEvent() 函数以确保线程将在调用 Wait() 函数之前终止。

我看不出它应该挂起的任何原因?

【问题讨论】:

  • 我很抱歉人们-所有答案似乎都是错误的。我使用 Sleep() 在我的代码中尝试了所有组合,我没有理由认为它应该挂起?
  • 为什么不排序为 m_event=Create... 然后 _beginthread?
  • 如果问题中的代码不正确,您需要编辑它 - 许多人指出了一个错误,然后您在评论中说它不存在。目前我很困惑,所以无能为力。
  • 你为什么不编译你的例子,看看它是否重现了这个问题?在我看来,它没有。这段代码没有任何错误,除了 m_event/m_thread 范围,我认为这只是您在示例中遗漏的内容。
  • 在提出此类问题时,几乎总是需要发布重现问题的可编译代码。通常很难诊断与计时相关的错误——为什么要把它变成一个猜谜游戏?如果你不能发布真正的代码(因为它太复杂,有太多的依赖,或者是专有的),那么把它减少到一个小的重现案例通常会有向你展示问题的好处,所以你不会甚至需要发布一个问题。

标签: c++ multithreading winapi


【解决方案1】:

问题在于您使用了 _beginthread API。您不能将此函数返回的句柄与 Win32 等待函数一起使用。您应该使用 _beginthreadex 或 CreateThread。来自 MSDN:

如果成功,这些函数中的每一个都会返回一个新创建线程的句柄;但是,如果新创建的线程退出太快,_beginthread 可能不会返回有效的句柄...

您可以...能够将 _beginthreadex 返回的线程句柄与同步 API 一起使用,而 _beginthread 则无法做到这一点。

【讨论】:

  • 你确定这个事实吗:你不能将这个函数返回的句柄与 Win32 等待函数一起使用
  • 是的,我刚刚在 MSDN 网站上查看过。很奇怪——我不知道。
  • beginthreadex() 似乎正在工作 - 现在它根本没有挂起
【解决方案2】:

我在代码中看不到任何问题(假设事件是在构造函数中启动线程之前创建的)。

  • 该事件是一个自动重置事件,并且 初始状态是无信号的。
  • 子线程必须等到 事件已发出信号。
  • 主线程将发出事件信号并 等待子进程终止。

假设这是完整的代码(不是示例代码),对我来说它看起来很不错。

建议你使用process Explorer来观察事件的状态。

编辑

在主线程等待线程句柄之前子线程被终止的可能性很小。如果句柄被其他一些内核对象重用,主线程将无限等待。 尝试在创建线程后使用DuplicateHandle 复制句柄,并在 WaitForSingleObject 中使用此句柄。

【讨论】:

  • 是的,事件是在创建线程之前构造的。是的,正如你所说,这段代码很好。上面的答案没有任何意义。
  • 您能否编辑相关代码以反映事件创建的顺序?
  • 句柄重复是很有可能的,因为 Windows 尝试重用句柄值。
  • 不要使用 _beginthread,改用 _beginthreadex。这样,您将负责关闭手柄。 (您需要关闭句柄,然后在等待后使用 CloseHandle)
  • 如果句柄关闭得太快而无法调用WaitForSingleObject,那么它也关闭得太快而无法调用DuplicateHandle。显然,一旦函数返回,_beginthread 的返回值就会被废弃。 (为什么会这样写?)
【解决方案3】:

你检查过线程句柄m_thread是否真的有效吗?

在某些情况下 _beginthread 会返回一个无效句柄 - 特别是当线程快速退出时(这里肯定会出现这种情况,因为线程可能会启动,通过等待(因为已经设置了事件)然后终止)。

使用 _beginthreadex 创建句柄,尽管您必须调用 _endthreadex 以确保所有内容都已清理完毕。

【讨论】:

  • 要清楚,在 Alan Moore 所指出的情况下,_beginthread() 的返回值是一个 有效 HANDLE,只是不再是这个特定线程的 HANDLE,或者甚至可能根本没有线程。如果它碰巧是一个永远不会发出信号的 HANDLE(例如典型的文件 HANDLE),则永远无法满足等待。
【解决方案4】:

您可能需要考虑使用SignalObjectAndWait 而不是单独的SetEvent()WaitForSingleObject() 调用,因为这会作为单个操作发生,并且如果无法发出事件信号会立即失败。

【讨论】:

  • 是的,我可以试试这个。谢谢。
  • 请注意,“信号”和“等待”不能保证作为原子操作执行。在调用 SignalObjectAndWait 的线程开始等待第二个对象之前,在其他处理器上执行的线程可以观察到第一个对象的信号状态。
  • @TripleS:你是对的。在我回答这个问题时,MSDN 页面将此功能描述为“以原子方式向一个对象发出信号并等待另一个对象”。我已相应地更正了答案。
【解决方案5】:

Hasturkun 说SignalObjectAndWait 是一个原子操作。

但是,MSDN 不同意:

注意“信号”和“等待”是 不保证被执行为 原子操作。执行的线程 其他处理器可以观察到 第一个对象的信号状态 在线程调用之前 SignalObjectAndWait 开始等待 第二个对象。

是的,我自己有点困惑,现在我的头很痛,但我相信我会弄清楚的。它可能不是原子的,但它似乎说它完成了确保您的线程在发出信号之前正在等待对象的对象。对吧?

【讨论】:

    【解决方案6】:

    如果SetEvent 在线程中的WaitforSingleObject 之前执行,则线程将挂起。所以在那种情况下WaitforSingleObject(m_event,INFINITE) 将永远等待。

    【讨论】:

    • 真的吗?我有一个 WFSO 进入的经验(它等待的事件已经发出信号)它将立即退出。
    【解决方案7】:

    在对象完全构造之前分发对象的地址不是坏习惯吗?我们可以控制新线程何时执行obj->ThreadEntry(),可能是在构造函数完成之后,有一个完整的对象可以调用 ThreadEntry,或者 ThreadEntry 可能在对象被构造之前开始。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-09-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-17
      • 2020-09-12
      • 2019-02-07
      相关资源
      最近更新 更多