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

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

Я хочу, чтобы "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;
    }
    
Другие вопросы по тегам