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() {
shared_lock<std::shared_mutex> lock(mtx);
unique_lock<std::mutex> uniqueLock(cond_mtx);
print("read lock 1");
++step;
cond.notify_all();
cond.wait(uniqueLock, []{
return step == 2;
});
uniqueLock.unlock();
shared_lock<std::shared_mutex> lock1(mtx);
print("read lock 2");
}
void write() {
unique_lock<std::mutex> uniqueLock(cond_mtx);
cond.wait(uniqueLock, []{
return step == 1;
});
uniqueLock.unlock();
lock_guard<std::shared_mutex> lock(mtx);
uniqueLock.lock();
print("write lock");
++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来代替读写锁。
|