在类构造函数中将此指针传递给CreateThread在线程过程中表现得很奇怪

时间:2012-07-25 16:28:13

标签: c++ windows multithreading constructor createthread

在我的一个类的构造函数中,我将Windows函数CreateThread称为最后一个操作。创建线程以立即执行,并将我的类的this指针传递为lpParameter

在线程过程中,我将传递回的参数转换为我的类的指针,并将其命名为pThis

我可以看到pThis指向与我调用this时传递的CreateThread指针的相同的内存位置。但是,如果我查看pThis->...访问的成员变量,它们都会有错误的值。

我期望在this指针所属的类中使用的this->member_variable的值与在线程的过程中编写pThis->member_variable时得到的值相同。

如果我在另一个成员函数中调用CreateThread构造函数),一切都正常。

因此问题:是否禁止从C ++类的构造函数中调用Windows函数CreateThread?如果是,问题是什么?

澄清:

1)我可以确认该对象始终存在。仅当整个程序结束时,对象才会超出范围。正如我已经说过的那样:从其他成员函数调用CreateThread确实有用。

2)纠正了“有线”错字,应该是“怪异”,对不起。

一些代码:

我尝试发布代码片段,将内容减少到最低限度,同时保留“有缺陷”的部分。

class CTimerW32 : public CTimer
{
    public:
        CTimerW32();
        ~CTimerW32();

    private:
        static void CALLBACK TimerCallback(LPVOID lpParam, BOOLEAN bReason);
        static DWORD WINAPI WaitCompletition(LPVOID lpParam);

    private:
        HANDLE m_hCompletitionEvent;
        HANDLE m_hCompletitionThread;
        bool m_bStartDeferred;
};

您可以放心地忽略基类CTimer,因为它只是一个抽象的基类,可以在不同的平台上构建。

CTimerW32::CTimerW32()
{
    m_bStartDeferred= false;
    m_hCompletitionEvent= CreateEvent(NULL, FALSE, FALSE, NULL);
    m_hCompletitionThread= CreateThread(NULL, 0, WaitCompletition, this, 0, NULL);
}

我可以看到m_hCompletitionEvent在调用CreateEvent之后有效。

DWORD WINAPI CTimerW32::WaitCompletition(LPVOID lpParam)
{
    CTimerW32* pThis;
    DWORD dwRet;

    pThis= (CTimerW32*)(lpParam);

    while (true) {
        // just wait for the completition event to be signaled
        dwRet= WaitForSingleObject(pThis->m_hCompletitionEvent, INFINITE);

        // ...
        if (pThis->m_bStartDeferred) {
            // ...
        }
    }

调用WaitForSingleObject时出现问题。如前所述,在创建线程期间,类CTimerW32(现在为pThis)的对象的this指针仍具有与this指针相同的值。但是pThis->m_hCompletitionEvent中的句柄似乎是随机数据。它不是在构造函数中调用CreateEvent后观察到的值。

2 个答案:

答案 0 :(得分:2)

在构造函数中创建线程应该不是问题。此外,在运行构造函数中的任何代码以创建线程之前,您的对象应该由初始化列表完全初始化,因此初始化可能不是问题。

您正在观看的对象可能超出范围,并且在您在新线程中观察它之前调用其析构函数。尝试使用 new 动态创建对象并查看是否仍然会发生这种情况,我敢打赌它不会因为当对象超出范围时不会被销毁。

显然,你应该在更高的范围内保留一个指向这个对象的指针,这样你最终也可以删除它:)

答案 1 :(得分:0)

借助Application Verifier,您可能会有最好的运气来调试此问题。如果打开程序的“基础”选项,它将启用PageHeap,当内存被释放时,它会立即出错。如果你正在堆栈分配定时器变量,那么你运气不好,但是应该可以在调试器中看到当你注意到损坏时,创建定时器的线程仍然在声明了CTimerW32函数。

最后,对于这个用例,Threadpool Timer APIs可能比创建自己的专用线程更容易,并且资源消耗更少。