在类构造函数中将此指针传递给 CreateThread 在线程过程中表现得很奇怪
Posted
技术标签:
【中文标题】在类构造函数中将此指针传递给 CreateThread 在线程过程中表现得很奇怪【英文标题】:Passing this pointer to CreateThread in class constructor behaves weird in thread procedure 【发布时间】:2012-07-25 16:28:13 【问题描述】:在我的一个类的构造函数中,我将 Windows 函数 CreateThread
作为最后一个操作。创建线程以立即执行,我将类的 this 指针作为lpParameter
传递。
在线程过程中,我将传递回的参数转换为我的类的指针并将其命名为pThis
。
我可以看到pThis
指向与我调用CreateThread
时传递的this
指针相同的内存位置。但是,如果我查看pThis->...
访问的成员变量,它们都有错误的值。
我希望this指针所属的类中使用的this->member_variable
的值与我在线程过程中写入pThis->member_variable
时得到的值相同。
如果我在另一个成员函数(不是构造函数)中调用CreateThread
,一切都会正常运行。
因此问题是:是否禁止从 C++ 类的构造函数中调用 Windows 函数CreateThread
?如果有,有什么问题?
澄清:
1) 我可以确认该对象始终存在。只有当整个程序结束时,对象才会超出范围。正如我已经说过的:从其他成员函数调用CreateThread
确实有效。
2) 更正了“有线”错字,应该是“奇怪”,抱歉。
一些代码:
我尝试发布代码 sn-ps 将事情减少到最低限度,同时保持“故障”部分。
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
后观察到的值。
【问题讨论】:
线程运行时对象是否仍然存在?到时候不是在原线程中被破坏了吗? 线程工作时,你确定对象一直存在吗? 您能否发布一些代码:pThis
的声明、您对CreateThread
的调用以及您在哪里将pThis
转换为您的班级类型? “奇怪”是什么意思?你期待什么价值观,你看到了什么?
【参考方案1】:
在构造函数中创建线程应该不是问题。此外,在运行构造函数中的任何代码以创建线程之前,您的对象应该由初始化列表完全初始化,因此初始化可能不是问题。
您正在观察的对象很可能超出范围,并且在您在新线程中观察它之前调用了它的析构函数。尝试使用 new 动态创建对象,看看是否仍然会发生这种情况,我敢打赌它不会,因为对象在超出范围时不会被销毁。
显然,您应该将指向该对象的指针保留在更高的范围内,以便最终也可以将其删除:)
【讨论】:
【参考方案2】:在Application Verifier 的帮助下调试这个问题你可能会很幸运。如果您为您的程序打开“基本”选项,它将启用 PageHeap,当内存被释放时会立即出错。如果您正在堆栈分配计时器变量,那么您的运气就不太好,但是如果在您注意到损坏时,应该可以在调试器中看到,创建计时器的线程仍在函数中声明了 CTimerW32 函数。
最后,对于这个用例,Threadpool Timer APIs 可能比创建自己的专用线程更容易工作,并且资源消耗更少。
【讨论】:
以上是关于在类构造函数中将此指针传递给 CreateThread 在线程过程中表现得很奇怪的主要内容,如果未能解决你的问题,请参考以下文章
没有指针,我可以在 Python 中将引用作为参数传递吗? [复制]