简体   繁体   English

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

[英]Passing this pointer to CreateThread in class constructor behaves weird in thread procedure

In the constructor of one of my classes I call the Windows function CreateThread as last operation. 在我的一个类的构造函数中,我将Windows函数CreateThread称为最后一个操作。 The thread is created to execute immediately and I pass the this pointer of my class as lpParameter . 创建线程立即执行,我将我的类的this指针作为lpParameter传递。

In the thread procedure I cast the parameter passed back to a pointer of my class and name it pThis . 在线程过程中,我将参数传递回我的类的指针并将其命名为pThis

I can see that pThis points to the same memory location as the this pointer I passed when I called CreateThread . 我可以看到pThis指向与我在调用CreateThread时传递的this指针相同的内存位置 However if I look at the member variables accessed by pThis->... they all have wrong values. 但是,如果我查看pThis->...访问的成员变量pThis->...它们都有错误的值。

I expected the value of this->member_variable used in the class to which the this pointer belongs to be the same as the one I get when writing pThis->member_variable in the thread's procedure. 我期望this指针所属的类中使用的this->member_variable的值与我在线程过程中编写pThis->member_variable时得到的值相同。

If I call CreateThread in another member function ( not the constructor) everything behaves correctly. 如果我在另一个成员函数( 而不是构造函数)中调用CreateThread ,则一切都正常。

Hence the question: Is it forbidden to call the Windows function CreateThread from within the constructor of a C++ class? 因此问题是:是否禁止从C ++类的构造函数中调用Windows函数CreateThread If yes, what is the problem? 如果是,问题是什么?

Clarification: 澄清:

1) I can confirm that the object always exists. 1)我可以确认该对象始终存在。 The object gets out of scope only when the entire program ends. 仅当整个程序结束时,对象才会超出范围。 As I already said: calling CreateThread from some other member function does work. 正如我已经说过的那样:从其他一些成员函数调用CreateThread确实有效。

2) Corrected the 'wired' typo, should have been 'weird', sorry. 2)纠正了'有线'错字,应该是'怪异',对不起。

Some code: 一些代码:

I try to post code snippets reducing things to the bare minimum while maintaining the 'faulty' parts. 我尝试发布代码片段,在保持“错误”部分的同时将事情减少到最低限度。

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;
};

You can safely ignore the baseclass CTimer as it is just an abstract baseclass enabling build on different platforms. 您可以安全地忽略基类CTimer因为它只是一个抽象基类,可以在不同平台上构建。

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

Here I can see that m_hCompletitionEvent is valid after the call to CreateEvent . 在这里我可以看到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) {
            // ...
        }
    }

Here things are going wrong in the call to WaitForSingleObject . 这里对WaitForSingleObject的调用出错了。 As already stated the this pointer of the object of class CTimerW32 (now pThis ) still has the same value as the this pointer during thread creation. 如前所述,类CTimerW32 (现在为pThis )的对象的this指针在创建线程期间仍具有与this指针相同的值。 However the handle in pThis->m_hCompletitionEvent seems to be random data. 但是, pThis->m_hCompletitionEvent的句柄似乎是随机数据。 It is not the value observed after the call to CreateEvent in the constructor. 它不是在构造函数中调用CreateEvent之后观察到的值。

Creating a thread in a constructor should not be an issue. 在构造函数中创建线程应该不是问题。 Also, your object should be fully initialized by the initializer list before any code in the constructor is run to create the thread, so initialization probably isn't an issue. 此外,在运行构造函数中的任何代码以创建线程之前,应由初始化程序列表完全初始化您的对象,因此初始化可能不是问题。

It is likely that the object you're watching is out of scope and its destructor is called prior to you observing it in the new thread. 您正在观察的对象可能超出范围,并且在您在新线程中观察它之前调用其析构函数。 Try creating the object dynamically with new and see if this still happens, I bet it doesn't as the object won't get destroyed when it falls out of scope. 尝试使用new动态创建对象并查看是否仍然发生这种情况,我敢打赌它不会因为当它超出范围时对象不会被销毁。

Obviously, you should keep a pointer to this object in a higher scope so you can eventually delete it too :) 显然,你应该在更高的范围内保留一个指向这个对象的指针,这样你最终也可以删除它:)

You will probably have the best luck debugging this problem with the aid of Application Verifier . 借助Application Verifier,您可能会有最好的运气来调试此问题。 If you turn on the "Basics" option for your program, it will enable PageHeap, which will fault immediately when memory gets freed. 如果打开程序的“基础”选项,它将启用PageHeap,当内存被释放时,它会立即出错。 If you're stack-allocating the timer variable, you're in less luck, but it should be possible to see in the debugger if at the time you notice the corruption, the thread which created the timer is still within the function where the CTimerW32 function was declared. 如果你正在堆栈分配定时器变量,那么你运气不好,但是应该可以在调试器中看到当你注意到损坏时,创建定时器的线程仍然在声明了CTimerW32函数。

Lastly, for this usecase, the Threadpool Timer APIs may work more easily, and with less resource consumption, than creating your own dedicated thread. 最后,对于这个用例, Threadpool Timer API可以比创建自己的专用线程更容易,并且消耗更少的资源。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM