2.9 KiB
Threads joining
А вы уже заметили, что в предыдущих заметках я использую std::jthread из C++20 вместо std::thread? И зачем?
А все очень просто: деструктор std::thread дурной.
Везде, где может начать вызываться деструктор std::thread, нужно втыкать
// std::thread t1;
if (t1.joinable()) { // Если вы не уверены в богатой жизненной истории
// объекта t1, обязательно выполняйте эту проверку
t1.join(); // или t1.detach()
}
Чтобы ознаменовать свое желание (или нежелание) дожидаться окончания выполнения потока.
Иначе деструктор потока повалит вашу программу, вызвав std::terminate.
Очень удобно и очень по RAII-шному, неправда ли?
Нет, конечно, совсем везде втыкать не надо. Если вы знаете, что кто-то другой уже выполнил это заклинание, или содержимое объекта std::thread переместили в другой объект (t2 = std::move(t1)).
И тем более не надо просто так втыкать этот код, обращающийся к одному и тому же объекту std::thread из разных потоков. Иначе — race condition. Надо синхронизировать.
И, конечно же, убедитесь что этот код ни в коем случае не будет выполняться параллельно с вызовом деструктора t1; Деструктор тоже вызывает joinable, а это опять race condition.
Собираетесь сделать обертку над std::thread, чтобы вызывать join в ее деструкторе? Спешу порадовать: join/detach кидают исключения. Со всеми вытекающими проблемами.
Здорово, да? Поэтому в примерах был и будет std::jthread. Его деструктор сам выполняет join и снимает хотя бы часть головной боли.
А если вас join не устраивает, не хотите ждать и пользуетесь detach... Ну что ж. Право ваше. Только помните, что все потоки резко и внезапно помрут, когда закончится main.