3

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

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

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

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

如果我调用CreateThread另一个成员函数(而不是构造函数),一切都会正常运行。

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

澄清:

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. 如前所述,类对象的 this 指针CTimerW32(现在pThis)在线程创建期间仍然具有与 this 指针相同的值。然而,其中的句柄pThis->m_hCompletitionEvent似乎是随机数据。它不是CreateEvent在构造函数中调用后观察到的值。

4

2 回答 2

2

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

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

显然,您应该将指向该对象的指针保持在更高的范围内,以便最终也可以将其删除:)

于 2012-07-25T16:36:18.960 回答
0

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

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

于 2012-08-20T02:36:02.127 回答