我不应该在线程过程中使用_endthreadex()来进行堆栈展开吗?

时间:2012-02-01 03:00:30

标签: c++ stack-unwinding

我在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()吗?

2 个答案:

答案 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 来包装原始指针,然后智能指针确保在范围结束后适当地释放对象内存。