我在这里遇到了一个非常惊人的问题。代码如下所示。
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()的调用中(在main函数内)。代码总是调用SetEvent()函数来确保线程将在调用Wait()函数之前终止。
我认为没有理由为什么要挂?
答案 0 :(得分:17)
问题是您使用_beginthread API。您不能使用Win32等待函数从此函数返回的句柄。您应该使用_beginthreadex或CreateThread。来自MSDN:
如果成功,则每个函数都返回新创建的线程的句柄;但是,如果新创建的线程退出太快,_beginthread可能不会返回有效的句柄...
你是......能够使用_beginthreadex返回的线程句柄和同步API,而你无法使用_beginthread。
答案 1 :(得分:2)
我没有在代码中看到任何问题(假设事件是在构造函数中启动线程之前创建的)。
假设这是完整的代码(不是示例代码),对我来说看起来很好。
我建议您使用process Explorer来观察事件的状态。
修改强>:
在主线程等待线程句柄之前,子线程有可能被终止。如果句柄被重用于其他一些内核对象,主线程将无限期等待。 尝试在创建线程后使用DuplicateHandle复制句柄,并在WaitForSingleObject中使用此句柄。
答案 2 :(得分:1)
您可能需要考虑使用SignalObjectAndWait而不是单独的SetEvent()
和WaitForSingleObject()
调用,因为这会作为单个操作发生,如果事件无法发出信号,则会立即失败。
答案 3 :(得分:1)
您是否检查过线程句柄m_thread是否实际有效?
在某些情况下,_beginthread将返回一个无效的句柄 - 特别是当线程快速退出时(这可能就是这里的情况,因为线程可以旋转,通过等待(因为事件已经设置)然后终止)。
使用_beginthreadex代替创建句柄,但您必须调用_endthreadex以确保清除所有内容。
答案 4 :(得分:0)
如果SetEvent
在线程中的WaitforSingleObject
之前执行,则线程将挂起。因此,在这种情况下,WaitforSingleObject(m_event,INFINITE)
将永远等待。
答案 5 :(得分:0)
在完全构造该对象之前分发对象的地址是不错的做法。我们可以控制新线程何时执行obj->ThreadEntry()
,可能是在构造函数完成之后,有一个完整的对象可以调用ThreadEntry,或者ThreadEntry可以在构造对象之前开始。
答案 6 :(得分:0)
Hasturkun说SignalObjectAndWait是原子操作。
然而,MSDN不同意:
请注意,“信号”和“等待”是 不能保证作为一个 原子操作。执行的线程 其他处理器可以观察到 第一个对象的信号状态 在线程调用之前 SignalObjectAndWait开始等待 第二个对象。
雅,我现在有点困惑,我的脑袋现在疼,但我相信我会弄清楚的。它可能不是原子的,但它似乎表明它确实在发出信号的对象发出信号之前确保你的线程正在等待对象。正确?