Разве я не должен использовать _endthreadex () в процедуре потока для разматывания стека? - PullRequest
0 голосов
/ 01 февраля 2012

Я изучал процедуру размотки стека в потоке в среде 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
манекен dtor
плохое распределение
манекен dtor

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

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

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

Ответы [ 2 ]

2 голосов
/ 01 февраля 2012

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

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

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

1 голос
/ 01 февраля 2012

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

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

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

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

Dummy dm;

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

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

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

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

...