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來代替讀寫鎖。