我在win32环境中检查了线程过程中的堆栈展开 我的测试代码如下。
class Dummy
{
public:
Dummy() { wcout << L"dummy ctor" << endl; }
~Dummy() { wcout << L"dummy dtor" << endl; }
};
void InnerFunc()
{
Dummy dm;
while(1)
{
char *buf = new char[100000000];
}
}
unsigned WINAPI ThreadFunc(void *arg)
{
Dummy dm;
try
{
InnerFunc();
}
catch(bad_alloc e)
{
wcout << e.what() << endl;
}
_endthreadex(0);
return 0;
}
void OuterFunc()
{
Dummy dm;
HANDLE hModule;
hModule = (HANDLE)_beginthreadex(0, 0, ThreadFunc, 0, 0, 0);
WaitForSingleObject(hModule, INFINITE);
CloseHandle(hModule);
}
int _tmain(int argc, _TCHAR* argv[])
{
OuterFunc();
wcout << e.what() << endl;
return 0;
}
输出结果:
假的人
假的人
假的人
假dtor
分配不好
虚拟的dtor
如您所知,构造函数和析构函数的输出未配对。我认为_endthreadex()使线程句柄发出信号并跳过线程的堆栈展开。
当我在没有_endthreadex()的情况下再次测试时,我能够得到我期望的结果。
在这种情况下,如果我需要在线程上进行堆栈展开,我不应该在线程过程中使用_endthreadex()吗?
答案 0 :(得分:2)
我猜想从不为在ThreadFunc中创建的实例调用析构函数。但是,您应该添加一种方法来区分每个构造函数和析构函数调用以确保。
假设发生了什么,似乎很清楚endthreadex会在不清理堆栈的情况下立即终止线程。文档明确声明在ThreadFunc返回时调用endthreadex,那么为什么还要在这里明确地调用它呢?
这绝对是我使用boost :: thread的情况。它将在线程创建和清理方面做正确的事情,而不会让您担心特定于win32的细节。
答案 1 :(得分:1)
你的问题是:
while(1)
{
char *buf = new char[100000000];
}
您创建了内存泄漏,在每次迭代中创建一个新对象,丢失对旧对象的任何引用。
Stack Unwinding,清除该范围内的所有本地对象,
Dummy dm;
是在InnerFunc()
内的本地存储上分配的对象,Stack Unwinding正确地销毁此对象,您看到的单个析构函数调用跟踪就是由此引起的。
堆栈展开不会显式释放动态内存。分配有new[]
的每个指针必须通过在同一地址上调用delete []
来显式解除分配。
我没有看到它与任何Windows线程函数有什么关系(我对Windows不太了解)但是我已经说过你有问题了。
<强>解决方案:强>
在例外情况下处理清理的简单解决方案是 RAII
您应该使用 Smart pointer 来包装原始指针,然后智能指针确保在范围结束后适当地释放对象内存。