Разве я не должен использовать _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;
}

Выходной результат:
фиктивный ctor
фиктивный ctor
фиктивный ctor
Dummy Dtor
плохое распределение
Dummy Dtor

Как вы знаете, выходные данные конструктора и деструктора не являются парными. Я думаю, что _endthreadex() позволяет сигнализировать дескриптор потока и пропускает разматывание стека потока.

Когда я снова протестировал без _endthreadex(), я смог получить ожидаемый результат.

В этом случае, если мне нужно разматывать стек в потоке, я не должен использовать _endthreadex() в процедуре потока?

2 ответа

Решение

Я предполагаю, что деструктор никогда не вызывается для экземпляра, созданного в ThreadFunc. Тем не менее, вы должны добавить способ различать каждый вызов конструктора и деструктора.

Предполагая, что это то, что происходит, кажется довольно ясным, что endthreadex немедленно завершает поток без очистки стека. В документах прямо указано, что endthreadex вызывается при возврате ThreadFunc, так зачем беспокоиться о его явном вызове здесь?

Это определенно тот случай, когда вместо этого я бы использовал boost::thread. Он будет работать правильно с точки зрения создания и очистки потоков, не беспокоясь о деталях, относящихся к win32.

Ваша проблема:

while(1)
{
    char *buf = new char[100000000];
}

Вы создали утечку памяти, на каждой итерации вы создаете новый объект, теряя любую ссылку на старый объект.

Разматывание стека, очищает все локальные объекты в этой области,

Dummy dm;

это объект, расположенный внутри локального хранилища InnerFunc()Разматывание стека справедливо уничтожает этот объект, и единственная трассировка вызова деструктора, которую вы видите, является следствием этого.

Разматывание стека явно не освобождает динамическую память. Каждый указатель выделен с new[] должен быть явно освобожден путем вызова delete [] по тому же адресу.

Я не вижу, как это связано с какой-либо из функций потоков Windows (я не очень разбираюсь в Windows), но, как я уже говорил, у вас есть проблема там.

Решение:
Простое решение для очистки во время исключений - RAII.
Вы должны использовать Smart-указатель, чтобы обернуть ваш необработанный указатель, а затем Smart-указатель гарантирует, что память вашего объекта будет должным образом освобождена, как только закончится область.

Другие вопросы по тегам