两个模板包装器
unique_lock (C++11)实现可移动的互斥体所有权包装器
写锁包装
shared_lock (C++14)实现可移动的共享互斥体所有权封装器
读锁包装
C++11读写锁
在C++11中需要自己实现读写锁,下面代码为xin_hen大佬包装好的代码使用了信号量进行实现;
没有效率验证,不能保证某些情况下是否比mutex性能高;
#pragma once
#include<mutex>
#include<condition_variable>
class ReadWriteLock {
private:
int readWaiting = 0; //等待读
int writeWaiting = 0; //等待写
int reading = 0; //正在读
int writing = 0; //正在写
std::mutex mx;
std::condition_variable cond;
bool preferWriter; //偏向读
public:
ReadWriteLock(bool isPreferWriter = false) :preferWriter(isPreferWriter) {}
void readLock() {
std::unique_lock<std::mutex>lock(mx);
++readWaiting;
cond.wait(lock, [&]() {return writing <= 0 && (!preferWriter || writeWaiting <= 0); });
++reading;
--readWaiting;
}
void writeLock() {
std::unique_lock<std::mutex>lock(mx);
++writeWaiting;
cond.wait(lock, [&]() {return reading <= 0 && writing <= 0; });
++writing;
--writeWaiting;
}
void readUnLock() {
std::unique_lock<std::mutex>lock(mx);
--reading;
//当前没有读者时,唤醒一个写者
if(reading<=0)
cond.notify_one();
}
void writeUnLock() {
std::unique_lock<std::mutex>lock(mx);
--writing;
//唤醒所有读者、写者
cond.notify_all();
}
};
使用方式:
// 写者优先
ReadWriteLock readWriteLock(true);
{ // 读锁
readWriteLock.readLock();
readWriteLock.readUnLock();
}
{ // 写锁
readWriteLock.writeLock();
readWriteLock.writeUnLock();
}
C++14读写锁shared_timed_mutex
shared_timed_mutex的原名为“共享超时互斥锁 ”,只是使用者称为读写锁;
普通调用 w/r
lock // 互斥 若互斥不可用则阻塞
unlock // 解锁互斥
lock_shared // 共享 若互斥不可用则阻塞
unlock_shared // 解锁互斥
立即返回调用 w/r (与普通调用不同,如果不能使用资源,则立即返回,不解锁)
try_lock // 尝试锁定互斥,若互斥不可用则返回
try_lock_shared // 尝试共享锁定互斥,若互斥不可用则返回
超时返回调用 w/r (for为等待多少时间,until为到指定到具体时间,)
try_lock_for(...);
try_lock_until(...);
try_lock_shared_for(...);
try_lock_shared_until(...);
想要深入研究C++14shared_timed_mutex,请进入详情页签
如果只是简单使用可以看下面的简单使用方式,我一直遵循28原则,所有的知识全部过一遍,弄清楚其中的20%,剩余的80%只要知道有这个东西,需要用时再学习;
使用方式:
#include <shared_mutex>
std::shared_timed_mutex stmtx;
普通使用:
{ // 写
stmtx.lock();
stmtx.unlock();
}
{ // 读
stmtx.lock_shared();
stmtx.unlock_shared();
}
包装使用:(注意作用范围为{ })
{ // 写
std::unique_lock<std::shared_timed_mutex> lcx(stmtx);
}
{ // 读
std::shared_lock<std::shared_timed_mutex> lcx(stmtx);
}
当调用lock()的时,如果有线程获取了共享锁,那么写线程会等待,直到所有共享锁完成释放,才锁定资源,进行修改;
当调用lock_shared()时,如果有写线程获取了互斥锁,那么需要等待
当调用lock_shared()时,如果有读线程获取共享锁,也会直接返回,获取成功。
使用案例:
#include <shared_mutex>
#include <thread>
#include <iostream>
std::shared_timed_mutex stmtx;
void ReadThread()
{
while (true)
{
stmtx.lock_shared();
std::cout << "thread id = " << std::this_thread::get_id() << " lock shared read" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(200));
stmtx.unlock_shared();
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
void WriteThread()
{
while (true)
{
stmtx.lock();
std::cout << "thread id = " << std::this_thread::get_id() << " lock write" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(500));
stmtx.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
int main()
{
for(int i = 0; i < 3; ++i)
{
std::thread t(ReadThread);
t.detach();
}
for(int i = 0; i < 2; ++i)
{
std::thread t(WriteThread);
t.detach();
}
getchar();
return 0;
}
C++17读写锁 shared_mutex
C++17的shared_mutex和C++14的shared_timed_mutex类似,只是少了超时控制;
在vscode中使用std::shared_lock<std::shared_mutex>会报错;命名空间 "std" 没有成员 "shared_mutex";
但是在Linux下使用-std=c++17,是可以通过编译的;
vscode希望你能改为shared_timed_mutex,具体原由不太清楚,可能是我自己原因;
普通调用 w/r
lock // 互斥 若互斥不可用则阻塞
unlock // 解锁互斥
lock_shared // 共享 若互斥不可用则阻塞
unlock_shared // 解锁互斥
立即返回调用 w/r (与普通调用不同,如果不能使用资源,则立即返回,不解锁)
try_lock // 尝试锁定互斥,若互斥不可用则返回
try_lock_shared // 尝试共享锁定互斥,若互斥不可用则返回
使用方式
#include <shared_mutex>
std::shared_mutex g_mutex;
注意作用域为 { } 作用域结束解锁
普通使用:
std::shared_mutex mutex_;
{ // 写
mutex_.lock;
...
mutex_.unlock;
}
{ // 读
mutex_.lock_shared();
...
mutex_.unlock_shared();
}
包装器使用:
{ // 写
std::unique_lock<std::shared_mutex> wLock(g_mutex);
}
{ // 读
std::shared_lock<std::shared_mutex> rLock(g_mutex);
}
shared_mutex为C++17出现
-std=c++17 可以通过编译
-std=c++2a 可以通过编译
注意:在class中使用锁,需要添加 关键字:mutable 描述锁,否则在const成员函数中使用锁会报错;
使用案例
#include <iostream>
#include <mutex> // 对于 std::unique_lock
#include <shared_mutex>
#include <thread>
class ThreadSafeCounter {
public:
ThreadSafeCounter() = default;
// 多个线程/读者能同时读计数器的值。
unsigned int get() const {
std::shared_lock<std::shared_mutex> lock(mutex_);
return value_;
}
// 只有一个线程/写者能增加/写线程的值。
void increment() {
std::unique_lock<std::shared_mutex> lock(mutex_);
value_++;
}
// 只有一个线程/写者能重置/写线程的值。
void reset() {
mutex_.lock_shared();
value_ = 0;
mutex_.unlock_shared();
}
private:
mutable std::shared_mutex mutex_;
unsigned int value_ = 0;
};
int main() {
ThreadSafeCounter counter;
auto increment_and_print = [&counter]() {
for (int i = 0; i < 3; i++) {
counter.increment();
std::cout << std::this_thread::get_id() << ' ' << counter.get() << '\n';
// 注意:写入 std::cout 实际上也要由另一互斥同步。省略它以保持示例简洁。
}
};
std::thread thread1(increment_and_print);
std::thread thread2(increment_and_print);
thread1.join();
thread2.join();
}