Ожидание завершения std::thread

Я пытаюсь изящно очиститься при завершении программы, поэтому я вызываю join() на std::thread, чтобы дождаться ее завершения. Это просто блокирует основной поток навсегда, но я не понимаю, почему, потому что рабочий поток представляет собой (почти) пустой цикл, подобный этому:

void GameLoop::Run()
{
    while (run)
    {
        //  Do stuff... 
    }   
    std::cout << "Ending thread...\n";
}

Я устанавливаю run в false перед joining, конечно. Теперь я подозреваю, что это как-то связано с тем, что это функция-член и вызывается при уничтожении объекта. Я создаю ветку так: runThread.reset(new thread(&GameLoop::Run, this));, где runThread это unique_ptr<std::thread> и член GameLoop. Вызов join() поступает в деструктор объекта GameLoop.

Может быть, поток цикла не может завершиться, если его объект находится в процессе уничтожения? Согласно отладчику, нить цикла задерживается в темных глубинах msvcr120d.dll. Если да, то как бы вы с этим справились?

Осторожно: новинка std::thread здесь!

Обновление: Это мой призыв присоединиться к деструктору:

run = false;
if (runThread->joinable())
{
    runThread->join();
}

Обновление 2: если я удалю join(), я получу исключение, вызванное ~thread()!


person Kristian D'Amato    schedule 13.11.2013    source источник
comment
Надеюсь, вы где-то установили для run значение false/0... Было бы неплохо определить его как volatile.   -  person Alex F    schedule 13.11.2013
comment
Вы когда-нибудь устанавливали run в false? Что делает присоединение к потоку, так это усыпляет вызывающий поток до тех пор, пока целевой поток не умрет (с его различными значениями), что вы и видите. Это не убивает сам поток.   -  person Blindy    schedule 13.11.2013
comment
Да, конечно. Сообщение печатается и все.   -  person Kristian D'Amato    schedule 13.11.2013
comment
Обновляя свой вопрос, также укажите, что возвращает thread::joinable, если поток не может быть присоединен, вам, очевидно, не следует присоединяться к нему. Что произойдет, если вы удалите соединение? Если программа закрывается нормально (проверьте диспетчер задач), это означает, что поток был успешно очищен компилятором.   -  person Blindy    schedule 13.11.2013
comment
Можете ли вы опубликовать соответствующий код для вашего деструктора?   -  person Zac Howland    schedule 13.11.2013
comment
@AlexFarber - еще лучше определить его как std::atomic. volatile не имеет переносимой семантики и бесполезен для синхронизации между потоками.   -  person Pete Becker    schedule 13.11.2013
comment
Если вы удалите функцию join(), вы получите исключение, поскольку объект потока не может быть уничтожен до тех пор, пока поток не завершится (за исключением случаев, когда для него было вызвано отключение).   -  person Mark Vincze    schedule 13.11.2013
comment
У меня нет проблем с тем, что вы описываете в вашем коде... а/7555dac44353ca50   -  person melak47    schedule 13.11.2013
comment
Хм, спасибо, я не понимаю, почему это происходит здесь. Думаю, это должно быть как-то связано с разрушением, потому что если я вызову код соединения до того, как программа не зависнет.   -  person Kristian D'Amato    schedule 13.11.2013


Ответы (2)


Конечно, когда вы join поток, это не приводит к его завершению. Он просто блокируется до тех пор, пока этот поток не умрет по естественным (или неестественным) причинам.

Чтобы изящно очистить многопоточное приложение, нужно каким-то образом сказать рабочему потоку, что пора умирать, а затем дождаться, когда смерть произойдет. Часть «ждать, пока не случится смерть» — это то, для чего предназначен join.

person John Dibling    schedule 13.11.2013
comment
Я устанавливаю run на false. Позвольте мне обновить вопрос. - person Kristian D'Amato; 13.11.2013

Ааа, видимо ошибка в библиотеке времени выполнения. Потоки не завершаются успешно в деструкторах статических объектов в соответствии с этим вопросом. Мой GameLoop — это нестатический объект, содержащийся в статическом GameSystem. Я проверю, правда ли это, и обновлю.

Ага, подтвердил. Мне просто повезло, что я столкнулся с ошибкой при первом использовании!

person Kristian D'Amato    schedule 13.11.2013
comment
Ну, вы также захотите убедиться, что run правильно работает в многопоточной среде. - person Lightness Races in Orbit; 14.11.2013
comment
Хорошо, спасибо, я изменил его на atomic‹bool›, но мне интересно: для такой маленькой переменной, как bool, есть ли реальная проблема сделать ее атомарной, если один поток только пишет, а другой только чтение? - person Kristian D'Amato; 14.11.2013
comment
@KristianD'Amato Нет, если вам удобно доверять компилятору, когда поведение нескольких потоков, обращающихся к неатомарной переменной, является явно неопределенным поведением в C ++ 11. - person JAB; 03.11.2015