互斥元层级
本文内容摘记与“C++并发编程实战”
#pragma once
#include<mutex>
/*
* 封装当前互斥元所在的层级的信息
* 对互斥元进行分层级管理
*/
class HierarchicalMutex
{
private:
std::mutex m_mutex;
unsigned long const m_hierarchy_value; //当前即将请求的互斥元所在层级
unsigned long m_previous_hierarchy_value; //当前互斥元前一个互斥元所在的层级
// 当前线程拥有该互斥元所在的层级
// thread_local 修饰的变量具有线程周期
// 这些变量在线程开始的时候被生成 线程结束时被销毁
// 每个线程拥有一个独立的变量实例
// 存储周期:automatic static dynamic thread
static thread_local unsigned long m_this_thread_hierarchy_value;
/*检查是否违反层级关系*/
/*当前线程所获取的互斥元的层级比即将请求的互斥元的层级低:则不允许其去请求高层次的互斥元*/
void check_for_hierarchy_violation()
{
// 当前线程的层级比m_hierarchy_value低
if (m_this_thread_hierarchy_value <= m_hierarchy_value) {
throw std::logic_error("mutex hierarchy violated");
}
}
/*请求到新的互斥元,更新当前互斥元的层级号为新的层级号,记录下当前层级号为历史(previous)层级号*/
void update_hierarchy_value()
{
m_previous_hierarchy_value = m_this_thread_hierarchy_value;
m_this_thread_hierarchy_value = m_hierarchy_value;
}
public:
// 创建互斥元对象的时候指定其所在的层级:m_hierarchy_value
explicit HierarchicalMutex(unsigned long value):m_hierarchy_value(value),m_previous_hierarchy_value(0)
{
}
// 加锁,更新层级号,更新前检查是否可请求
void lock()
{
check_for_hierarchy_violation();
m_mutex.lock();
update_hierarchy_value();
}
void unlock()
{
m_this_thread_hierarchy_value = m_previous_hierarchy_value;
m_mutex.unlock();
}
bool try_lock()
{
check_for_hierarchy_violation();
if (!m_mutex.try_lock())
return false;
update_hierarchy_value();
return true;
}
};
// 初始化为最大值,表示任意层级的互斥元(HierarchicalMutex对象)都可以被锁定
// thread_local作用: 每个线程都有属于关于m_this_thread_hierarchy_value的副本
// 在一个线程中该变量m_this_thread_hierarchy_value的状态完全独立于另外一个线程中
// 读取的该变量的状态
thread_local unsigned long HierarchicalMutex::m_this_thread_hierarchy_value(ULONG_MAX);
// 检查规则
// 刚开始m_this_thread_hierarchy_value的值为最大值:所以所有线程都可以绕过检查
// 线程1 创建并使用HierarchicalMutex对象对其某部分代码进行加锁,此时,指定其层级号为M
// 此时更新 m_this_thread_hierarchy_value=M
// 线程2 创建并使用HierarchicalMutex对象对其某部分代码进行加锁,此时,指定其层级号为N
// 若N<M
// 允许线程2进行加锁,执行代码
// 若N>=M
// 则线程2不允许使用HierarchicalMutex对象对其某部分代码进行加锁,程序抛出异常
// 只要若N<M的线程都允许进行资源的访问(这一点很重要!!!)
// 层级加锁的方式比 直接对资源进行std::mutex.lock()和std::mutex::unlock()的方式好
// 因为层级加锁允许多方修改(增删查看) 而std::mutex.lock()和std::mutex::unlock()只允许一个线程进行访问
// !!!!!!! 很重要
// 关于m_previous_hierarchy_value的说明
// 保存当前线程之前的层次有助于在unlock()时对其进行恢复
// 否则就无法再次锁定一个更高层次的互斥元(换句话说,就是无法使用更高层次的
// HierarchicalMutex对其想要加锁的代码进行互斥化访问)
// 直到m_this_thread_hierarchy_value的值变为ULONG_MAX,则说明所有加锁的线程都执行结束了
// try_lock的说明
// m_mutex调用try_lock失败:则无法拥有这个锁,就无法更新层次值,并返回false
// 原理和lock()函数相同