У меня есть вопрос/сомнение по поводу следующего фрагмента книги «Параллелизм C++». Я хочу знать, есть ли техническая причина(ы) для размещения t.join()
после блока try catch вместо помещения его внутри блока try.
//callable whose defintion is not related to this question
struct my_func;
void f()
{
int some_local_state = 0;
func my_func(some_local_state);
std::thread t(my_func);
try {
do_something_in_current_thread();
//can we put/move the t.join() here? instead of having it at point #2
} catch (...) {
t.join();
throw;
}
//author put the t.join() here why not inside try block
t.join(); //#2
}
Как видите, автор поставил t.join()
в точку #2
. Но я хочу знать, почему бы нам просто не разместить его внутри самого блока try
, сразу после вызова do_something_in_current_thread()
. Я имею в виду, есть ли какие-либо технические случаи/преимущества размещения его в #2
вместо внутри try
блока, о которых я, возможно, не знаю, поскольку я только начал изучать многопоточность.
Как новичок, я не могу придумать ни одного случая/преимущества наличия его в #2
вместо этого внутри блока try.
Обратите внимание: я знаю, что в следующем разделе книги также есть jthread
, но я не ищу обходного пути. Вместо этого я спрашиваю только о данном коде.
Некоторые рекомендации имеют тенденцию минимизировать код в блоке try
(меньше кода для проверки на исключение).
Кстати, thread::join может сам генерировать исключение.
почему бы не внутри блока try
Вы можете переместить второй join()
в блок try
, поскольку оба пути приведут к присоединению к потоку, так же, как если бы join()
сохранялся в конце функции.
Нет никаких технических причин отдавать предпочтение одному другому.
Обратите внимание, что в C++20 этой ситуации можно избежать, используя вместо этого автоматическое объединение std::jthread:
void f() {
int some_local_state = 0;
func my_func(some_local_state);
std::jthread t(my_func);
do_something_in_current_thread();
}
@Jarod42 Да, если join()
выпадает в этом сценарии, должно быть, происходит что-то действительно подозрительное :-)
Я неправильно понял, кого join
тронуло это предложение.
Если вы хотите, чтобы что-то произошло в условиях броска или отсутствия броска, вы можете использовать защиту с оптическим прицелом. Минимальный пример этого:
#include <thread>
#include <functional>
void do_something_in_current_thread()
{};
class simple_scope_guard
{
public:
simple_scope_guard(std::function<void> fn) :
guard{ fn }
{
}
~simple_scope_guard()
{
guard();
}
private:
std::function<void> guard;
};
struct my_func;
void f()
{
int some_local_state = 0;
std::thread t([] {});
// start a scope to manage the lifetime of the scope guard
{
simple_scope_guard scope_guard{ [&] { t.join(); } };
do_something_in_current_thread();
// whether do_something throws or not scope_guard will go out of scope and
// call thread.join
}
}
Я проголосовал против, потому что ОП прямо сказал: «Обратите внимание, что я знаю, что в последнем разделе книги также есть jthread, но я не ищу обходной путь. Вместо этого я спрашиваю только о данном коде». Им не интересно знать другие пути решения этого вопроса. Вместо этого они задали конкретный вопрос по поводу данного фрагмента.
На мой взгляд, защита области - это не обходной путь, а широко используемый шаблон (в сценариях try/catch, наконец). Но не стесняйтесь понижать голосование, если считаете, что это обходной путь;)
Я хочу сказать, что ОП уже знает об охране области действия. Им не нужна защита области или что-то еще, кроме информации о размещении t.join()
в цитируемой ссылке из книги.
Читаю вопрос еще раз: вы правы. Я оставлю это здесь для других
Почему отрицательные голоса. В этом вопросе нет ничего плохого. Я разместил код в виде текста, указал источник и четко выразил свои сомнения.