Разве я не должен использовать _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-указатель гарантирует, что память вашего объекта будет должным образом освобождена, как только закончится область.