此代码异常是否正确处理?

时间:2017-07-26 08:44:58

标签: c++ exception-handling condition-variable unique-lock

在下面的代码中,事件可能抛出异常,甚至可能在处理程序中处理,(很少见,但仍然如此)

我希望在执行事件时保持“lck2”解锁,因为我不希望主线程块为“mtx2”,原因只不过是优化。

我可以保证在catch块中始终释放“lck2”吗?或者可能存在运行时异常,因此可能导致死锁或某些意外行为?

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 个答案:

答案 0 :(得分: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; })将暂停执行,只有在醒来并且 invoketrue时才会返回,然后我们可以推断出我们只需要该表达式等待。因此,我们可以放弃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;
    }