在 C++ 中进行异常处理时锁保护的用例

Deb*_*ish 0 c++ exception

我正在学习使用c++ lock_guard. 网上资源说我们不需要unlock手动,其次在出现异常的情况下mutex会自动释放,以便其他线程可以继续。

我正在尝试为第二种情况找到一个例子。基本上,当一个线程出现异常然后另一个线程应该继续时,我试图找到用例。

std::mutex m;
void f1() {
    lock_guard<std::mutex> lock(m);
    // some task  that may raise exception
}
void f2() {
    lock_guard<std::mutex> lock(m);
    // some other task
}
int main() {
    std::thread T1(f1);   
    T1.detach();

    std::thread T2(f2);   
    T2.join();
}
Run Code Online (Sandbox Code Playgroud)

我在里面尝试了除以零的算术f1。但它使整个程序崩溃。然后我尝试在内部分配一个非常大的内存(例如new int[100000000000]f1。然后整个程序也崩溃了bad_alloc

std::mutex m;
int a,b;
void f1() {
    lock_guard<std::mutex> lock(m);
    a = 1;
    int * ptr = new int[10000000000]; // too large
    b = 2;
}
void f2() {
    lock_guard<std::mutex> lock(m);
    cout << a <<" : "<<b <<endl;
}
int main() {
    std::thread T1(f1);   
    T1.detach();

    std::thread T2(f2);   
    T2.join();
}
Run Code Online (Sandbox Code Playgroud)

错误:

terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
Aborted (core dumped)
Run Code Online (Sandbox Code Playgroud)

如果我在有问题的代码段周围使用 try-catch 块,则thread2执行并且程序不会突然终止。但是现在T1没有释放锁(正如预期的那样try catch block)。

std::mutex m;
int a,b;
void f1() {
    lock_guard<std::mutex> lock(m);
    a = 1;
    try {
        int * ptr = new int[10000000000];
    }catch(...) {
        cout <<"new faild"<<endl;
    }
    // still locked
    std::this_thread::sleep_for(std::chrono::milliseconds(2000)); //2s
    b = 2;
}
void f2() {
    lock_guard<std::mutex> lock(m);
    cout << a <<" : "<<b <<endl;
}
int main() {
    std::thread T1(f1);   
    T1.detach();

    std::thread T2(f2);   
    T2.join();
}
Run Code Online (Sandbox Code Playgroud)

我也不相信try-catch block上述情况,因为不使用的重点mutex.lock()/unlock()是优雅地处理和释放互斥锁。

我错过了什么吗?请举一个例子,一个线程发生异常(一些常见的异常情况),互斥锁被释放,其他线程继续执行。此外,主程序也不应该终止。

谢谢!

Mil*_*nek 8

std::lock_guard是一个非常简单的类。它看起来像这样:

template <typename T>
class lock_guard
{
public:
    lock_guard(T& mtx)
        : mtx_{mtx}
    {
        mtx_.lock();
    }

    ~lock_guard()
    {
        mtx_.unlock();
    }

    // not copyable
    lock_guard(const lock_guard&) = delete;
    lock_guard& operator=(const lock_guard&) = delete;

private:
    T& mtx_;
};
Run Code Online (Sandbox Code Playgroud)

如您所见,它所做的只是在其构造函数中锁定互斥锁并在其析构函数中解锁它。

这很有用,因为当对象析构函数因任何原因超出范围时会被调用,包括抛出异常时(如果它在某处被捕获;未捕获的异常将终止应用程序):

std::mutex mutex;

void thread_func()
{
    try {
        std::lock_guard<std::mutex> guard{mutex};
        // mutex is now locked
        throw std::exception{};
    } catch (...) {
        // mutex is already unlocked here.
    }
    // mutex is also unlocked here.
}
Run Code Online (Sandbox Code Playgroud)

  • @Debashish - 当您在 try 块中添加 `return` 语句以退出函数并忘记解锁互斥锁时会发生什么?真正的代码很复杂,并且有很多控制流。RAII 允许您防止它变得棘手。 (4认同)