目录

一、 Mutex.h

1. Mutex.h 的整体定位

2. 逐模块拆解核心实现

1. 线程安全静态注解(Clang 专属)

2. MCHECK 宏:系统调用错误检查

3. MutexLock 类:原生互斥锁封装(核心)

4. MutexLockGuard 类:RAII 自动加解锁(易用性核心)

5. 防误用宏:禁止临时对象

3. 核心使用示例

4. 核心设计亮点

总结


一、 Mutex.h

先贴出完整代码,再逐部分解释:

// 本源代码的使用受 BSD 风格许可证约束
// 该许可证可在 License 文件中查阅。
//
// 作者:陈硕 (chenshuo at chenshuo dot com)

#ifndef MUDUO_BASE_MUTEX_H
#define MUDUO_BASE_MUTEX_H

#include "muduo/base/CurrentThread.h"  // 获取当前线程 ID(tid)
#include "muduo/base/noncopyable.h"    // 不可拷贝基类
#include <assert.h>                    // 断言(调试期检查逻辑)
#include <pthread.h>                   // POSIX 线程库:pthread_mutex_t 及相关操作

// 线程安全注解开始 {
// 参考文档:https://clang.llvm.org/docs/ThreadSafetyAnalysis.html
// 作用:通过注解辅助 Clang 静态分析工具检测线程安全问题(如未加锁访问受保护数据)

// 仅在 Clang 编译器下启用线程安全注解,其他编译器下注解为空(无副作用)
#if defined(__clang__) && (!defined(SWIG))
#define THREAD_ANNOTATION_ATTRIBUTE__(x)   __attribute__((x))
#else
#define THREAD_ANNOTATION_ATTRIBUTE__(x)   // 非 Clang 编译器,注解无操作
#endif

// 标记类/对象为「互斥锁能力」(表示该对象是一个锁)
#define CAPABILITY(x) \
  THREAD_ANNOTATION_ATTRIBUTE__(capability(x))

// 标记类为「作用域锁」(如 MutexLockGuard,构造加锁、析构解锁)
#define SCOPED_CAPABILITY \
  THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)

// 标记变量受指定锁保护(访问时必须持有该锁)
#define GUARDED_BY(x) \
  THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))

// 标记指针变量指向的对象受指定锁保护
#define PT_GUARDED_BY(x) \
  THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))

// 标记锁的获取顺序:当前锁必须在指定锁之前获取
#define ACQUIRED_BEFORE(...) \
  THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))

// 标记锁的获取顺序:当前锁必须在指定锁之后获取
#define ACQUIRED_AFTER(...) \
  THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))

// 标记函数需要持有指定锁才能调用
#define REQUIRES(...) \
  THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))

// 标记函数需要持有指定锁的共享模式才能调用
#define REQUIRES_SHARED(...) \
  THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))

// 标记函数会获取指定锁
#define ACQUIRE(...) \
  THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))

// 标记函数会以共享模式获取指定锁
#define ACQUIRE_SHARED(...) \
  THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))

// 标记函数会释放指定锁
#define RELEASE(...) \
  THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))

// 标记函数会以共享模式释放指定锁
#define RELEASE_SHARED(...) \
  THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))

// 标记函数尝试获取指定锁(可能失败)
#define TRY_ACQUIRE(...) \
  THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))

// 标记函数尝试以共享模式获取指定锁(可能失败)
#define TRY_ACQUIRE_SHARED(...) \
  THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))

// 标记函数执行期间不会持有指定锁(排除锁竞争)
#define EXCLUDES(...) \
  THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))

// 断言当前线程持有指定锁
#define ASSERT_CAPABILITY(x) \
  THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))

// 断言当前线程持有指定锁的共享模式
#define ASSERT_SHARED_CAPABILITY(x) \
  THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))

// 标记函数返回的锁对象
#define RETURN_CAPABILITY(x) \
  THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))

// 禁用当前函数的线程安全分析
#define NO_THREAD_SAFETY_ANALYSIS \
  THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)

// 线程安全注解结束 }

#ifdef CHECK_PTHREAD_RETURN_VALUE
// 开启 pthread 函数返回值检查(调试模式)
#ifdef NDEBUG
// 非调试模式:声明断言失败处理函数(输出错误信息并终止程序)
__BEGIN_DECLS
extern void __assert_perror_fail (int errnum,
                                  const char *file,
                                  unsigned int line,
                                  const char *function)
    noexcept __attribute__ ((__noreturn__));
__END_DECLS
#endif

// MCHECK 宏:检查 pthread 函数返回值,失败则触发断言
// errnum != 0 时调用 __assert_perror_fail,输出错误码、文件、行号、函数名
#define MCHECK(ret) ({ __typeof__ (ret) errnum = (ret);         \
                       if (__builtin_expect(errnum != 0, 0))    \
                         __assert_perror_fail (errnum, __FILE__, __LINE__, __func__);})

#else  // CHECK_PTHREAD_RETURN_VALUE
// 关闭 pthread 函数返回值检查(仅用 assert 验证)
#define MCHECK(ret) ({ __typeof__ (ret) errnum = (ret);         \
                       assert(errnum == 0); (void) errnum;})

#endif // CHECK_PTHREAD_RETURN_VALUE

namespace muduo
{

// MutexLock 使用示例:作为类的成员变量
//
// class Foo
// {
//  public:
//   int size() const;
//
//  private:
//   mutable MutexLock mutex_;          // mutable 允许 const 成员函数加锁
//   std::vector<int> data_ GUARDED_BY(mutex_); // 标记 data_ 受 mutex_ 保护
// };
// CAPABILITY("mutex"):标记 MutexLock 是互斥锁类型(供 Clang 分析)
class CAPABILITY("mutex") MutexLock : noncopyable
{
 public:
  // 构造函数:初始化 pthread 互斥锁,初始化持有线程 ID 为 0
  MutexLock()
    : holder_(0)
  {
    // 初始化互斥锁(默认属性),MCHECK 检查初始化是否成功
    MCHECK(pthread_mutex_init(&mutex_, NULL));
  }

  // 析构函数:销毁互斥锁
  ~MutexLock()
  {
    // 断言:析构时锁未被任何线程持有(防止解锁前析构)
    assert(holder_ == 0);
    // 销毁互斥锁,MCHECK 检查销毁是否成功
    MCHECK(pthread_mutex_destroy(&mutex_));
  }

  // 检查当前线程是否持有该锁(必须在锁已加锁时调用,用于断言)
  bool isLockedByThisThread() const
  {
    return holder_ == CurrentThread::tid();
  }

  // 断言当前线程持有该锁(供调试期检查)
  // ASSERT_CAPABILITY(this):标记该函数要求持有当前锁
  void assertLocked() const ASSERT_CAPABILITY(this)
  {
    assert(isLockedByThisThread());
  }

  // 内部使用的方法(供 MutexLockGuard/Condition 调用)

  // 加锁:获取互斥锁,并记录持有线程 ID
  // ACQUIRE():标记该函数会获取锁
  void lock() ACQUIRE()
  {
    MCHECK(pthread_mutex_lock(&mutex_));
    assignHolder(); // 记录当前线程为锁的持有者
  }

  // 解锁:释放互斥锁,并清空持有线程 ID
  // RELEASE():标记该函数会释放锁
  void unlock() RELEASE()
  {
    unassignHolder(); // 清空锁的持有者
    MCHECK(pthread_mutex_unlock(&mutex_));
  }

  // 获取 pthread_mutex_t 原生指针(供 Condition 调用)
  // 非 const:因为 pthread_cond_wait 需要修改互斥锁状态
  pthread_mutex_t* getPthreadMutex() /* non-const */
  {
    return &mutex_;
  }

 private:
  // 友元类:Condition 需要访问 MutexLock 的原生互斥锁和 UnassignGuard
  friend class Condition;

  // 内部辅助类:临时解除锁的线程持有关系(供 Condition::wait 使用)
  // 构造时清空 holder_,析构时恢复 holder_ 为当前线程 ID
  class UnassignGuard : noncopyable
  {
   public:
    explicit UnassignGuard(MutexLock& owner)
      : owner_(owner)
    {
      owner_.unassignHolder(); // 清空锁的持有者
    }

    ~UnassignGuard()
    {
      owner_.assignHolder(); // 恢复锁的持有者为当前线程
    }

   private:
    MutexLock& owner_; // 关联的 MutexLock 对象
  };

  // 清空锁的持有者(holder_ 置 0)
  void unassignHolder()
  {
    holder_ = 0;
  }

  // 设置锁的持有者为当前线程(记录 tid)
  void assignHolder()
  {
    holder_ = CurrentThread::tid();
  }

  pthread_mutex_t mutex_; // POSIX 原生互斥锁对象
  pid_t holder_;          // 持有该锁的线程 ID(0 表示未被持有)
};

// MutexLockGuard 使用示例:作为栈上局部变量(RAII 管理锁)
// int Foo::size() const
// {
//   MutexLockGuard lock(mutex_); // 构造时加锁,析构时自动解锁
//   return data_.size();
// }
// SCOPED_CAPABILITY:标记该类是作用域锁(构造加锁、析构解锁)
class SCOPED_CAPABILITY MutexLockGuard : noncopyable
{
 public:
  // 构造函数:关联 MutexLock 并加锁
  // ACQUIRE(mutex):标记该函数会获取 mutex 锁
  explicit MutexLockGuard(MutexLock& mutex) ACQUIRE(mutex)
    : mutex_(mutex)
  {
    mutex_.lock();
  }

  // 析构函数:自动解锁(RAII 核心,确保作用域结束释放锁)
  // RELEASE():标记该函数会释放锁
  ~MutexLockGuard() RELEASE()
  {
    mutex_.unlock();
  }

 private:
  MutexLock& mutex_; // 关联的 MutexLock 对象(引用,不持有所有权)
};

}  // namespace muduo

// 防止误用宏:禁止创建匿名的 MutexLockGuard 对象
// 错误用法:MutexLockGuard(mutex_); // 临时对象会立即析构,锁被提前释放
// 正确用法:MutexLockGuard lock(mutex_); // 命名对象,作用域结束才析构
#define MutexLockGuard(x) error "Missing guard object name"

#endif  // MUDUO_BASE_MUTEX_H
    1. Mutex.h 的整体定位

    Mutex.h 是 Muduo 多线程同步的基石,核心解决原生 POSIX 互斥锁的三大问题:

    • 易用性差:需手动调用 pthread_mutex_lock/unlock,忘记解锁会导致死锁;
    • 安全性低:无持有线程检查,跨线程解锁、重复解锁会引发未定义行为;
    • 调试困难:系统调用失败(如 pthread_mutex_init)时静默执行,难以排查。

    Muduo 的解决方案是:

    • MutexLock:封装原生互斥锁,记录持有线程 ID,增加断言检查;
    • MutexLockGuard:RAII 封装,栈上创建自动加锁,析构自动解锁;
    • 静态注解:给 Clang 编译器提供线程安全分析,静态检查锁的误用;
    • MCHECK 宏:检查所有 pthread 调用返回值,失败时断言并打印错误。
    2. 逐模块拆解核心实现
    1. 线程安全静态注解(Clang 专属)

    这部分宏是给 Clang Thread Safety Analysis 用的,目的是静态编译期检查锁的使用错误(如未加锁访问受保护数据、跨线程解锁),其他编译器会忽略这些宏(无副作用)。

    核心注解宏解析:

    作用 示例
    CAPABILITY("mutex") 标记类是一个 “锁能力”(如 MutexLock) class CAPABILITY("mutex") MutexLock
    SCOPED_CAPABILITY 标记类是 “作用域锁”(如 MutexLockGuard) class SCOPED_CAPABILITY MutexLockGuard
    GUARDED_BY(x) 标记数据成员受 x 锁保护 std::vector<int> data_ GUARDED_BY(mutex_)
    ASSERT_CAPABILITY(x) 断言当前线程持有 x 锁 void assertLocked() const ASSERT_CAPABILITY(this)
    ACQUIRE() 标记函数获取锁 void lock() ACQUIRE()
    RELEASE() 标记函数释放锁 void unlock() RELEASE()

    示例效果:如果代码中未加锁就访问 data_ GUARDED_BY(mutex_),Clang 编译时会直接报错,提前发现线程安全问题,无需运行时调试。

    2. MCHECK 宏:系统调用错误检查

    封装 pthread 函数的返回值检查,是 Muduo 保证系统调用正确性的核心:

    #ifdef CHECK_PTHREAD_RETURN_VALUE
    // 非NDEBUG模式:调用__assert_perror_fail,打印错误并终止
    #define MCHECK(ret) ({ __typeof__ (ret) errnum = (ret);         \
                           if (__builtin_expect(errnum != 0, 0))    \
                             __assert_perror_fail (errnum, __FILE__, __LINE__, __func__);})
    #else
    // NDEBUG模式:仅断言,无额外开销
    #define MCHECK(ret) ({ __typeof__ (ret) errnum = (ret);         \
                           assert(errnum == 0); (void) errnum;})
    #endif
    • __builtin_expect(errnum != 0, 0):编译器优化,提示 “errnum != 0” 是小概率事件,提升分支预测效率;
    • 覆盖所有 pthread 调用(如 pthread_mutex_init/lock/unlock/destroy),避免系统调用失败后静默执行(如互斥锁初始化失败却继续使用)。
    3. MutexLock 类:原生互斥锁封装(核心)
    class CAPABILITY("mutex") MutexLock : noncopyable
    {
     public:
      // 构造:初始化互斥锁,holder_置0(无线程持有)
      MutexLock() : holder_(0) { MCHECK(pthread_mutex_init(&mutex_, NULL)); }
    
      // 析构:断言holder_=0(锁已释放),销毁互斥锁
      ~MutexLock() { assert(holder_ == 0); MCHECK(pthread_mutex_destroy(&mutex_)); }
    
      // 断言:当前线程是否持有该锁(用于调试)
      bool isLockedByThisThread() const { return holder_ == CurrentThread::tid(); }
    
      // 强制断言:必须当前线程持有锁(防跨线程操作)
      void assertLocked() const ASSERT_CAPABILITY(this) { assert(isLockedByThisThread()); }
    
      // 加锁:封装pthread_mutex_lock,记录持有线程ID
      void lock() ACQUIRE() { MCHECK(pthread_mutex_lock(&mutex_)); assignHolder(); }
    
      // 解锁:清空持有线程ID,封装pthread_mutex_unlock
      void unlock() RELEASE() { unassignHolder(); MCHECK(pthread_mutex_unlock(&mutex_)); }
    
      // 暴露原生互斥锁(给Condition用)
      pthread_mutex_t* getPthreadMutex() { return &mutex_; }
    
     private:
      friend class Condition; // 允许Condition访问getPthreadMutex
    
      // 内部类:临时解除holder_(给Condition::wait()用)
      class UnassignGuard : noncopyable {
       public:
        explicit UnassignGuard(MutexLock& owner) : owner_(owner) { owner_.unassignHolder(); }
        ~UnassignGuard() { owner_.assignHolder(); }
       private: MutexLock& owner_;
      };
    
      // 清空持有线程标记
      void unassignHolder() { holder_ = 0; }
      // 设置持有线程为当前线程
      void assignHolder() { holder_ = CurrentThread::tid(); }
    
      pthread_mutex_t mutex_; // 原生POSIX互斥锁
      pid_t holder_;          // 持有锁的线程ID(CurrentThread::tid())
    };

    核心设计细节:

    • noncopyable 继承:禁止拷贝 —— 互斥锁是系统资源句柄,拷贝会导致多个 MutexLock 操作同一个 pthread_mutex_t,引发死锁 / 未定义行为;
    • holder_ 成员
      • 记录持有锁的线程 ID(CurrentThread::tid() 是当前线程的轻量级 ID);
      • 析构时断言 holder_ == 0:确保锁已释放,避免 “持有锁时销毁互斥锁”;
      • isLockedByThisThread()/assertLocked():防止跨线程解锁(如线程 A 加锁,线程 B 解锁),这是原生互斥锁的常见陷阱;
    • UnassignGuard 内部类:给 Condition::wait() 专用 ——pthread_cond_wait 会原子释放锁,但 holder_ 仍标记为当前线程,UnassignGuard 临时清空 holder_,析构时恢复,保证 holder_ 语义正确;
    • 仅暴露必要接口lock()/unlock() 封装原生调用,getPthreadMutex() 仅给 Condition 用,对外隐藏原生句柄,减少误用。
    4. MutexLockGuard 类:RAII 自动加解锁(易用性核心)
    class SCOPED_CAPABILITY MutexLockGuard : noncopyable
    {
     public:
      // 构造:传入MutexLock引用,自动加锁
      explicit MutexLockGuard(MutexLock& mutex) ACQUIRE(mutex) : mutex_(mutex) { mutex_.lock(); }
    
      // 析构:自动解锁(栈对象析构时触发,无论是否抛异常)
      ~MutexLockGuard() RELEASE() { mutex_.unlock(); }
    
     private:
      MutexLock& mutex_; // 引用:绑定一个MutexLock,避免拷贝
    };

    核心价值(RAII 模式):

    • 自动加解锁:栈上创建 MutexLockGuard 时加锁,函数 / 作用域结束时析构解锁,彻底避免 “忘记解锁”;
    • 异常安全:即使代码中抛出异常,栈对象仍会析构,保证锁被释放,避免死锁;
    • 禁止拷贝:防止锁被意外传递(如函数返回 MutexLockGuard,导致提前解锁)。
    5. 防误用宏:禁止临时对象
    // 防止用户写:MutexLockGuard(mutex_); (临时对象立即析构,解锁)
    #define MutexLockGuard(x) error "Missing guard object name"

    错误示例(无该宏时):

    void Foo::func() {
      MutexLockGuard(mutex_); // 临时对象,构造加锁后立即析构解锁!
      // 后续代码无锁保护,线程不安全
      data_.push_back(1);
    }

    该宏会让编译器直接报错,强制用户命名对象:

    MutexLockGuard lock(mutex_); // 正确:对象lock在作用域内,直到结束才解锁
    3. 核心使用示例

    正确用法

    #include "muduo/base/Mutex.h"
    #include <vector>
    
    class Foo {
    public:
      int size() const {
        MutexLockGuard lock(mutex_); // RAII加锁,作用域结束解锁
        return data_.size();
      }
    
      void add(int x) {
        MutexLockGuard lock(mutex_);
        data_.push_back(x);
      }
    
    private:
      mutable MutexLock mutex_;       // mutable:const成员函数也能加锁
      std::vector<int> data_ GUARDED_BY(mutex_); // 标记受mutex_保护
    };
    
    int main() {
      Foo foo;
      foo.add(1);
      return foo.size();
    }

    错误用法(被宏禁止)

    void Foo::badFunc() {
      MutexLockGuard(mutex_); // 编译器报错:Missing guard object name
      data_.push_back(2);
    }

    错误用法(被断言禁止)

    void Foo::badUnlock() {
      mutex_.lock();
      // 跨线程解锁(线程A加锁,线程B解锁)
      // 调用mutex_.unlock()时,assert(isLockedByThisThread())失败,程序终止
    }
    4. 核心设计亮点
    • RAII 彻底解决解锁问题MutexLockGuard 栈上使用,自动管理锁的生命周期,异常安全、无需手动解锁;
    • 线程持有检查holder_ 记录持有线程,assertLocked() 防止跨线程解锁 / 操作,从调试层面规避陷阱;
    • 静态编译期检查:Clang 注解提前发现锁的误用(如未加锁访问受保护数据),无需运行时调试;
    • 零额外开销(Release 模式)
      • MCHECK 在 NDEBUG 下仅为 assert,无函数调用;
      • 注解宏在非 Clang 编译器下为空,不影响性能;
      • holder_ 是内存对齐的,无缓存行问题;
    • 极简封装:仅保留核心功能,无冗余接口,符合 Muduo “极简高效” 的设计理念。
    总结
    1. 核心分工
      • MutexLock:封装原生互斥锁,记录持有线程,提供加解锁底层实现;
      • MutexLockGuard:RAII 封装,栈上使用,自动加解锁,是用户的主要使用方式;
    2. 安全保障
      • MCHECK 检查系统调用错误,避免静默失败;
      • holder_ 断言防止跨线程操作;
      • 防误用宏禁止临时对象导致的提前解锁;
      • Clang 注解静态检查锁的使用;
    3. 核心陷阱规避
      • 忘记解锁 → RAII 自动解锁;
      • 跨线程解锁 → holder_ 断言;
      • 临时对象误解锁 → 防误用宏;
      • 系统调用失败 → MCHECK 检查。

    Logo

    有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

    更多推荐