C++14支持std::shared_timed_mutex
C++17支持std::shared_mutex
前者相比后者支持的操作更多,但是后者相对性能更好。
- 使用
std::lock_guard<std::shared_mutex>
和std::unique_lock<std::shared_mutex>
互斥访问 - 使用
std::shared_lock<std::shared_mutex>
实现共享访问(C++14),使用方式和std::unique_lock
相同
多个线程可以同时共享访问std::shared_mutex
,但是如果在读锁上获取写锁,会使得写锁阻塞,直到所有读锁释放,同时写锁也会阻塞后面的读锁防止写锁饥饿。
假如一个线程A的函数需要读锁1,其内部运行的某个函数也需要读锁2,在线程A得到读锁1后另一个线程B需要写锁,线程B写锁上锁以后会阻塞等待线程A释放读锁1,线程A继续向下运行,等到第二次拿读锁2的时候,为了避免不断读锁上锁造成对写锁的饥饿,读锁2会阻塞等待线程B写锁释放,因此造成了死锁。
我自己写了一个小的Demo,的确会造成死锁。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <shared_mutex>using namespace std;void print() {cout << "\n";
}
template<typename T, typename... Args>
void print(T&& first, Args&& ...args) {cout << first << " ";print(std::forward<Args>(args)...);
}std::shared_mutex mtx;
int step = 0;
std::mutex cond_mtx;
std::condition_variable cond;void read() {//step0: 读锁shared_lock<std::shared_mutex> lock(mtx);unique_lock<std::mutex> uniqueLock(cond_mtx);print("read lock 1");//通知step0结束++step;cond.notify_all();//等待step1: 写锁 结束cond.wait(uniqueLock, []{return step == 2;});uniqueLock.unlock();//step2: 再次读锁shared_lock<std::shared_mutex> lock1(mtx);print("read lock 2");
}void write() {//等待step0: 读锁 结束unique_lock<std::mutex> uniqueLock(cond_mtx);cond.wait(uniqueLock, []{return step == 1;});uniqueLock.unlock();//step1: 写锁lock_guard<std::shared_mutex> lock(mtx);uniqueLock.lock();print("write lock");//通知step1结束++step;cond.notify_all();uniqueLock.unlock();}int main() {std::thread t_read{read};std::thread t_write{write};t_read.join();t_write.join();return 0;
}
为了避免死锁,应该像陈硕大神建议的那样使用智能指针+互斥锁实现copy on write来代替读写锁。