我不应该在线程过程中使用 _endthreadex() 来进行堆栈展开吗?
我检查了 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;
}
输出结果:
虚拟演员
虚拟演员
虚拟演员
虚拟数据传输器
分配错误
dummy dtor
如您所知,构造函数和析构函数的输出不是配对的。我认为 _endthreadex() 使线程句柄发出信号并跳过线程的堆栈展开。
当我在没有 _endthreadex() 的情况下再次测试时,我能够得到我期望的结果。
在这种情况下,如果我需要在线程上展开堆栈,我是否应该在线程过程中使用 _endthreadex() ?
I examined about stack unwinding on thread procedure in win32 environment.
My test code is the following.
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;
}
Output result:
dummy ctor
dummy ctor
dummy ctor
dummy dtor
bad allocation
dummy dtor
As you know, an output of constructor and destructor is not paired. I think that _endthreadex() makes the thread handle be signaled and skips stack unwinding of the thread.
When I tested again without _endthreadex(), I was able to get a result I expected.
In this case, if I need stack unwinding on thread, shouldn't I use _endthreadex() in thread procedure?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我猜想在 ThreadFunc 中创建的实例永远不会调用析构函数。但是,您应该添加一种方法来区分每个构造函数和析构函数调用以确保安全。
假设这就是发生的情况,很明显 endthreadex 立即终止线程而不清理堆栈。文档明确指出当 ThreadFunc 返回时调用 endthreadex,那么为什么要在这里显式调用它呢?
这绝对是我会使用 boost::thread 的情况。它将在线程创建和清理方面做正确的事情,而不用担心 win32 特定的细节。
I would guess the destructor is never called for the instance created in ThreadFunc. However, you should add a way to distinguish each constructor and destructor call to be sure.
Assuming that's what's happening, it seems pretty clear that endthreadex terminates the thread immediately without cleaning up the stack. The docs explicitly state that endthreadex is called when ThreadFunc returns, so why bother calling it explicitly here?
This is definitely a case where I'd use boost::thread instead. It will do the right thing in terms of thread creation and cleanup without making you worry about the win32-specific details.
您的问题是:
您已经创建了内存泄漏,在每次迭代中您创建的新对象都会丢失对旧对象的任何引用。
堆栈展开,清除该范围内的所有本地对象,
是在
InnerFunc()
内的本地存储上分配的对象,堆栈展开正确地销毁这个对象和您看到的单个析构函数调用跟踪就是由于这个原因。堆栈展开不会显式释放动态内存。每个使用
new[]
分配的指针都必须通过在同一地址上调用delete []
来显式释放。我不明白它与任何 Windows 线程函数有什么关系(我对 Windows 不太了解),但正如我已经说过的,你在那里遇到了问题。
解决方案:
在异常期间处理清理的简单解决方案是RAII。
您应该使用智能指针来包装您的原始指针和智能指针可确保在作用域结束后正确地释放对象内存。
Your problem is:
You have created a memory leak, on each iteration you create a new object losing any reference to the old object.
Stack Unwinding, clears off all the local objects in that scope,
is a object allocated on local storage inside
InnerFunc()
, Stack Unwinding rightly destroys this object and the single destructor call trace you see is due to this.Stack Unwinding does not explicitly deallocate the dynamic memory. Each pointer allocated with
new[]
will have to be explicitly deallocated by calling adelete []
on the same address.I don't see how it is related to any of the Windows thread functions(I am not much in to windows) but as I already stated you have a problem there.
Solution:
The simple solution to handling cleanups during exceptions is RAII.
You should use a Smart pointer to wrap your raw pointer and then the Smart pointer ensures that your object memory gets appropriately deallocated once the scope ends.