C++ 中的(共享)互斥体

(Shared) mutex in C++

我看过共享互斥体的示例:

class MyData {
    std::vector<double> data_;
    mutable shared_mutex mut_;   // the mutex to protect data_;

public:
    void write() {
        unique_lock<shared_mutex> lk(mut_);
        // ... write to data_ ...
    }

    void read() const {
        shared_lock<shared_mutex> lk(mut_);
        // ... read the data ...
    }
};

自然我会写成:

public:
    void write() {
        mut_.lock();
        // ... write to data_ ...
        mut_.unlock();
    }

    void read() const {
        mut_.lock_shared();
        // ... read the data ...
        mut_.unlock_shared();
    }
};

我的方法是否也正确?我使用的和示例中使用的有区别吗?另外,一个比另一个有优势吗?谢谢!

Is my way also correct?

考虑如果互斥锁的锁定和解锁之间的代码抛出异常会发生什么:

void write() {
    mut_.lock();
    // <-- exception is thrown here
    mut_.unlock();
}

然后互斥锁保持锁定状态。

are there advantages of one over the other?

是的,unique_lock<>RAII idiom 之后,因此在出现异常时自动处理互斥体的解锁(即由其析构函数处理):

void write() {
    unique_lock<shared_mutex> lk(mut_);
    // <-- exception is thrown
}

如果在创建 unique_lock<shared_mutex> 对象后发生异常 – lk – 调用其析构函数,然后解锁关联的互斥锁(如果它被锁定)(记住 std::unique_lock,与 std::lock_guard 不同,并不总是拥有关联互斥体上的锁的所有权——请参阅 std::defer_lock and std::unique_lock::unlock())。

综上所述,使用lock_guard/unique_lock/shared_lock,在出现异常或从不同执行路径离开成员函数时,您不需要进行特殊处理.

RAII 版本 unique_lock() 通常会避免原始互斥锁,这在两种情况下更安全:

  • 异常
  • 过早return

一般会避免使用原始指针,而使用 RAII 版本的智能指针:unique_ptr 或 shared_ptr。

无论哪种情况,RAII 版本都确保互斥量(或指针)在超出范围时始终被释放。