C++ 层级锁

层级锁的思想就是:

  • 只能向低层加锁
  • 只能向高层解锁

这保证了线程间按照顺序加锁、解锁,防止死锁的产生

#include <cstddef>
#include <mutex>
#include <stdexcept>

class hierarchical_mutex {
public:
    explicit hierarchical_mutex(const std::size_t hierarchical) :
        hierarchy_value_(hierarchical),
        previous_hierarchy_value_(0) {
    }

    void lock() {
        // 检查是否违反层级规则
        // 这里由于只访问 thread_local 变量(hierarchy_value_ 不会变),所以不需要加锁
        check_for_hierarchy_violation();

        // 加锁
        internal_mutex_.lock();

        // 更新层级信息,这里需要加锁
        update_hierarchy_value();
    }

    void unlock() {
        // 只能依次从最低层级往高层级解锁
        if (this_thread_hierarchy_value_ != hierarchy_value_) {
            throw std::logic_error("mutex hierarchy violated");
        }

        // 复原层级信息,其实就是把 head 指向 next
        this_thread_hierarchy_value_ = previous_hierarchy_value_;

        // 解锁
        internal_mutex_.unlock();
    }

    bool try_lock() {
        check_for_hierarchy_violation();
        if (!internal_mutex_.try_lock()) return false;
        update_hierarchy_value();
        return true;
    }

private:
    void check_for_hierarchy_violation() const {
        if (this_thread_hierarchy_value_ <= hierarchy_value_) {
            // 不能往高层加锁,只能逐渐往低层加锁
            throw std::logic_error("mutex hierarchy violated");
        }
    }

    void update_hierarchy_value() {
        // 保存之前的最低层级到 previous_hierarchy_value_
        // 由于一个锁只能由一个线程加锁,所以我们可以一直根据 previous_hierarchy_value_ 找到当前线程所有的锁
        // this_thread_hierarchy_value_ 就相当于 head,previous_hierarchy_value_ 就相当于 next
        previous_hierarchy_value_ = this_thread_hierarchy_value_;

        // 更新当前线程所拥有的最低层级
        this_thread_hierarchy_value_ = hierarchy_value_;
    }

private:
    std::mutex internal_mutex_;

    // 该层级锁的层级值
    const std::size_t hierarchy_value_;

    // 保存之前的该层级锁的上一个加锁线程的 this_thread_hierarchy_value_
    std::size_t previous_hierarchy_value_;

    // 注意这里是 thread_local 的,每个线程都记录当前线程所加锁的最低层级
    static thread_local std::size_t this_thread_hierarchy_value_;
};

这个设计的非常巧妙,使用类似链表的方式来维护当前线程的所有锁