В следующем коде возможно, что событие вызывает исключение, и оно может не обрабатываться даже обработчиком (редко, но это все еще так)
Я хочу, чтобы «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;
}
}
Переписывание вашего кода, вероятно, было бы более уместным, чтобы другому разработчику было легче работать над кодом. Я покажу вам две переписывает:
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;
}
Других решений пока нет …