Правильно ли обрабатываются исключения из этого кода?

В следующем коде возможно, что событие вызывает исключение, и оно может не обрабатываться даже обработчиком (редко, но это все еще так)

Я хочу, чтобы «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;
    }
    
1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]