Правильно ли обрабатываются исключения из этого кода?
В следующем коде возможно, что событие вызывает исключение, и оно может не обрабатываться даже обработчиком (редко, но это все еще так)
Я хочу, чтобы "lck2" был разблокирован во время выполнения события, потому что я не хочу блокировать основной поток для "mtx2", причина - не более чем оптимизация.
Могу ли я гарантировать, что "lck2" всегда освобождается в блоке catch? или могут быть исключения во время выполнения, и, следовательно, это может вызвать взаимные блокировки или непредвиденное поведение?
std::unique_lock<std::mutex>lck2(mtx2); // lock used for waiting for event.
while (_isRunning)
{
try
{
while (_isRunning)
{
// cvar2 is condition variable
cvar2.wait(lck2, [&] {return invoke; }); // wait until invoke == true
if (invoke) // if event must be invoked
{
lck2.unlock();
OnEvent(this, someproperty); // may throw exception
lck2.lock();
invoke = false; // execution completed
}
}
}
catch (...) // we need to keep this thread alive at all costs!
{
lck2.lock(); // is this safe?
invoke = false;
}
}
1 ответ
Переписывание вашего кода, вероятно, было бы более уместным, чтобы другому разработчику было легче работать над кодом. Я покажу вам две переписывает:
Во-первых, (плохо)
while (true) { try { { std::lock_guard<std::mutex> lckx(mtx2); if(!_isRunning) break; //out of the main loop } bool should_invoke = false; { std::unique_lock<std::mutex> lck2(mtx2); cvar2.wait(lck2, [&] {return invoke; }); should_invoke = invoke; } if (should_invoke) // if event must be invoked { OnEvent(this, someproperty); // may throw exception { std::lock_guard<std:mutex> lckx(mtx2); invoke = false; // execution completed } } } catch (...) // we need to keep this thread alive at all costs! { std::lock_guard<std:mutex> lckx(mtx2); invoke = false; } }
Во-вторых, (хорошо)
Разбиение (первого) кода на более мелкие функциональные единицы; отметим также, что выражение
cvar2.wait(lck2, [&]{ return invoke; })
приостановит выполнение и вернется только в случае пробуждения иinvoke
являетсяtrue
тогда мы можем сделать вывод, что нам нужно только это выражение, чтобы ждать. Следовательно, мы можем отказаться от излишнего использованияinvoke
, Отсюда имеем:void do_work(){ while(is_running()){ try{ wait_for_invocation(); OnEvent(this, someproperty); // may throw exception set_invocation_state(false); catch(...){ set_invocation_state(false); } } }
Где определены помощники:
bool is_running(){ std::lock_guard<std::mutex> lckx(mtx2); return _isRunning; } void wait_for_invocation(){ std::unique_lock<std::mutex> lck2(mtx2); cvar2.wait(lck2, [&] {return invoke; }); } void set_invocation_state(bool state){ std::lock_guard<std::mutex> lckx(mtx2); invoke = state; }