说明
C++14中引入std::shared_mutex
std::shared_mutex用于管理可转移和共享所有权的互斥对象,适用场景比较特殊:一个或多个读线程同时读取共享资源,且只有一个写线程来修改这个资源,这种情况下才能从shared_mutex获取性能优势。
- shared_lock是read lock。搭配std::shared_mutex使用,被锁后仍允许其他线程执行同样被shared_lock的代码。
//shared_lock构造函数中调用了shared_mutex的lock_shared函数
explicit shared_lock(mutex_type& _Mtx)
: _Pmtx(_STD addressof(_Mtx)), _Owns(true) { // construct with mutex and lock shared
_Mtx.lock_shared();
}
- lock_guard和unique_lock是write lock。被锁后不允许其他线程执行被shared_lock或unique_lock的代码。
//lock_guard以及unique_lock构造函数中调用了shared_mutex的lock函数
explicit lock_guard(_Mutex& _Mtx) : _MyMutex(_Mtx) { // construct and lock
_MyMutex.lock();
}
//unique_lock
explicit unique_lock(_Mutex& _Mtx) : _Pmtx(_STD addressof(_Mtx)), _Owns(false) { // construct and lock
_Pmtx->lock();
_Owns = true;
}
- std::shared_mutex 相比于mutex多提供了一个lock_shared函数,用于读锁。
测试
#include <shared_mutex>
#define READ_THREAD_COUNT 8
#define LOOP_COUNT 5000000
class TimeConsum
{
public:
TimeConsum() { m_nTimeStart = std::chrono::high_resolution_clock::now(); };
operator double&& () const noexcept
{
auto end = std::chrono::high_resolution_clock::now();
return std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(end - m_nTimeStart).count();
}
private:
std::chrono::high_resolution_clock::time_point m_nTimeStart;
};
using ReadLock = std::shared_lock<std::shared_mutex> ;
using WriteLock = std::lock_guard<std::shared_mutex> ;
using NormalLock = std::lock_guard<std::mutex>;
class SharedMutexCounter
{
public:
SharedMutexCounter() = default;
unsigned int get() const
{
//被mutable修饰的变量即使在const函数中也可以修改
ReadLock lock(mutex);
return value;
}
void increment()
{
WriteLock lock(mutex);
value++;
}
std::string name()const
{
return "shared";
}
private:
//在C++中,mutable也是为了突破const的限制而设置的。
//被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中。
mutable std::shared_mutex mutex;
unsigned int value = 0;
};
class MutexCounter
{
public:
MutexCounter() = default;
unsigned int get() const
{
NormalLock lock(mutex);
return value;
}
void increment()
{
NormalLock lock(mutex);
value++;
}
std::string name()const
{
return "normal";
}
private:
mutable std::mutex mutex;
unsigned int value = 0;
};
template <typename COUNTER>
void test_mutex()
{
TimeConsum cons;
std::atomic_int32_t temp{ 0 };
COUNTER counter;
const auto writer = [&counter]() {for (int i = 0; i < LOOP_COUNT; ++i)counter.increment(); };
const auto reader = [&counter, &temp]() {for (int i = 0; i < LOOP_COUNT; ++i)temp = counter.get(); };
std::vector<std::thread>readlist;
for (int i = 0; i < READ_THREAD_COUNT; ++i)
{
readlist.emplace_back(reader);
}
thread wr(writer);
for (int i = 0; i < readlist.size(); ++i)
{
readlist[i].join();
}
wr.join();
std::cout << "Test "<< counter.name()<<", count:[" << counter.get() << "]" << ",get:[" << temp << "]" << std::endl;
std::cout << "Const time:[" << cons << "]" << std::endl;
}
void shared_lock_test()
{
test_mutex<MutexCounter>();
test_mutex<SharedMutexCounter>();
}
//output
Test normal, count:[5000000],get:[5000000]
Const time:[3821.9]
Test shared, count:[5000000],get:[5000000]
Const time:[2274.59]