C++ WaitForSingleObject()
我陷入了一个非常惊人的问题,代码如下C++ WaitForSingleObject(),c++,multithreading,winapi,C++,Multithreading,Winapi,我陷入了一个非常惊人的问题,代码如下 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 t
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;
}
问题:
当上面的代码运行时,有时(五分之一)会挂起,并且控件在调用WaitforSingleObject()时(在主函数内部)会卡住。代码始终调用SetEvent()函数,以确保线程在调用Wait()函数之前终止
我看不出它为什么会挂起?我看不出代码中有任何问题(假设事件是在构造函数中启动线程之前创建的)
- 该事件是一个自动重置事件,并且 初始状态为无信号状态
- 子线程必须等到 事件发出信号
- 主线程将向事件发送信号,然后 等待子进程终止
尝试在创建线程后使用复制句柄,并在WaitForSingleObject中使用此句柄 > P>您可能想考虑使用替代的<<代码> SETEVER()/<代码>和<代码> WaIfFutSunLobObjor()/Case>调用,因为这是作为单个操作发生的,如果无法发出信号,则会立即失败。 您是否检查了线程句柄m_线程是否实际有效 在某些情况下,_beginthread将返回无效句柄-特别是当线程快速退出时(这里肯定是这种情况,因为线程可能会旋转,通过等待(因为事件已经设置),然后终止)
使用_beginthreadex来创建句柄,尽管您必须调用_endthreadex以确保所有内容都已清理。问题在于您使用了_BeginThreadAPI。不能将此函数返回的句柄与Win32 wait函数一起使用。您应该使用_beginthreadex或CreateThread。从MSDN: 如果成功,这些函数中的每一个都会向新创建的线程返回一个句柄;但是,如果新创建的线程退出得太快,_beginthread可能不会返回有效的句柄 你是。。。能够将_beginthreadex返回的线程句柄与同步API一起使用,这是_beginthread无法做到的
如果在线程中的
WaitforSingleObject
之前执行SetEvent
,则线程将挂起。因此,在这种情况下,WaitforSingleObject(m_event,INFINITE)
将永远等待。在对象完全构造之前分发对象的地址是否是一种好的做法。我们可以控制新线程何时执行obj->ThreadEntry()
,它可能是在构造函数完成之后,有一个完整的对象可以调用ThreadEntry,或者ThreadEntry可能是在对象被构造之前开始的。Hasturkun说这是一个原子操作
然而,MSDN不同意:
请注意,“信号”和“等待”是
不保证作为一个整体执行
原子操作。在上执行的线程
其他处理器可以观察到
第一个对象的信号状态
在线程调用之前
SignalObjectAndWait开始等待
第二个对象
是的,我自己也有点困惑,我的头现在很痛,但我相信我会找到答案的。它可能不是原子的,但它似乎表明,它完成了确保线程在对象发出信号之前等待对象的任务。对吗?我很抱歉,人们-所有的答案似乎都错了。我使用Sleep()尝试了代码中的所有组合,我认为它没有理由挂起?为什么不按m_event=Create排序。。。那么_beginthread?如果问题中的代码不正确,您需要对其进行编辑-许多人都指出了一个错误,而您在评论中说的错误并不存在。现在我很困惑,所以没办法。你为什么不编译你的例子,看看它是否再现了这个问题?在我看来,事实并非如此。这段代码没有什么问题,除了m_事件/m_线程作用域,我认为这只是您在示例中遗漏的一部分。当问这类问题时,几乎总是需要发布可编译代码来重新处理问题。通常很难诊断与时间相关的bug——为什么要把它变成猜测游戏呢?如果你不能发布真正的代码(因为它太复杂,有太多的依赖项,或者是专有的),那么将它缩减到一个小的复制案例通常会有一个额外的好处,让你看到问题,所以你甚至不需要发布问题。是的,事件是在创建线程之前构建的。是的,正如你所说的,这段代码很好。上面的答案没有任何意义。你能编辑有问题的代码以反映事件创建的顺序吗?当Windows试图重用句柄值时,句柄复制是很有可能的。不要使用_beginthread,而是使用_beginthreadex。这样,您将负责关闭手柄。(等待后需要关闭句柄,然后使用CloseHandle)如果句柄关闭得太快,无法调用WaitForSingleObject,那么它也关闭得太快,无法调用DuplicateHandle。显然,一旦函数返回,_beginthread的返回值就可能过时。(为什么会这样写?)是的,我可以试试这个。谢谢。注意“信号”和“等待”不能保证作为原子操作执行。在其他处理器上执行的线程可以在调用SignalObjectAndWait的线程开始对第二个对象进行等待之前观察第一个对象的信号状态。@TripleS:您是正确的。在我回答这个问题时,MSDN页面描述了这一点