当前位置: 首页 > news >正文

C++ mutex的实现源码分析

博主介绍:程序喵大人

  • 35- 资深C/C++/Rust/Android/iOS客户端开发
  • 10年大厂工作经验
  • 嵌入式/人工智能/自动驾驶/音视频/游戏开发入门级选手
  • 《C++20高级编程》《C++23高级编程》等多本书籍著译者
  • 更多原创精品文章,首发gzh,见文末
  • 👇👇记得订阅专栏,以防走丢👇👇
    😉C++基础系列专栏
    😃C语言基础系列专栏
    🤣C++大佬养成攻略专栏
    🤓C++训练营
    👉🏻个人网站

本文档基于GCC 5.4.0源码,深入分析C++ std::mutex的实现原理和内部机制。

C++11引入的mutex是多线程编程中的核心同步原语,用于保护共享资源不被多个线程同时访问。

整体架构

分层架构设计

C++ mutex的实现采用了分层架构:

应用层: std::mutex, std::recursive_mutex, std::timed_mutex↓
抽象层: __mutex_base, __recursive_mutex_base↓
平台层: __gthread_mutex_t (gthr-posix.h)↓
系统层: pthread_mutex_t (POSIX) / 其他平台特定实现

核心文件结构

  • libstdc++-v3/include/std/mutex: C++ mutex标准接口实现
  • libgcc/gthr.h: 线程抽象层接口定义
  • libgcc/gthr-posix.h: POSIX线程实现
  • libgcc/gthr-win32.h: Windows线程实现

基础类型分析

1. __mutex_base 基类

class __mutex_base
{
protected:typedef __gthread_mutex_t __native_type;#ifdef __GTHREAD_MUTEX_INIT__native_type _M_mutex = __GTHREAD_MUTEX_INIT;constexpr __mutex_base() noexcept = default;
#else__native_type _M_mutex;__mutex_base() noexcept{__GTHREAD_MUTEX_INIT_FUNCTION(&_M_mutex);}~__mutex_base() noexcept { __gthread_mutex_destroy(&_M_mutex); }
#endif__mutex_base(const __mutex_base&) = delete;__mutex_base& operator=(const __mutex_base&) = delete;
};

设计要点:

  • 禁用拷贝构造和赋值操作,确保mutex的唯一性
  • 支持两种初始化方式:静态初始化(INIT)和动态初始化(INIT_FUNCTION)
  • 使用RAII原则管理底层mutex资源

2. __recursive_mutex_base 递归mutex基类

class __recursive_mutex_base
{
protected:typedef __gthread_recursive_mutex_t __native_type;#ifdef __GTHREAD_RECURSIVE_MUTEX_INIT__native_type _M_mutex = __GTHREAD_RECURSIVE_MUTEX_INIT;__recursive_mutex_base() = default;
#else__native_type _M_mutex;__recursive_mutex_base(){__GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION(&_M_mutex);}~__recursive_mutex_base(){ __gthread_recursive_mutex_destroy(&_M_mutex); }
#endif
};

递归mutex特性:

  • 允许同一线程多次获取同一个mutex
  • 必须调用相同次数的unlock()才能真正释放锁
  • 内部维护持有计数和线程ID信息

std::mutex 实现详解

核心接口实现

class mutex : private __mutex_base
{
public:typedef __native_type* native_handle_type;void lock(){int __e = __gthread_mutex_lock(&_M_mutex);if (__e)__throw_system_error(__e);}bool try_lock() noexcept{return !__gthread_mutex_trylock(&_M_mutex);}void unlock(){__gthread_mutex_unlock(&_M_mutex);}native_handle_type native_handle(){ return &_M_mutex; }
};

操作语义分析

1. lock() 操作
  • 阻塞语义: 如果mutex已被占用,调用线程将阻塞等待
  • 错误处理: 通过异常机制处理错误情况
2. try_lock() 操作
  • 非阻塞语义: 立即返回,不会阻塞调用线程
  • 返回值: 成功获取锁返回true,否则返回false
  • noexcept: 保证不抛出异常
3. unlock() 操作
  • 释放语义: 释放当前线程持有的锁
  • 未定义行为: 如果当前线程未持有锁,行为未定义
  • 性能考虑: 通常不检查错误以提高性能

平台抽象层 (gthr)

POSIX实现 (gthr-posix.h)

typedef pthread_mutex_t __gthread_mutex_t;
typedef pthread_mutex_t __gthread_recursive_mutex_t;#define __GTHREAD_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER
#define __GTHREAD_ONCE_INIT PTHREAD_ONCE_INIT

关键映射关系:

  • __gthread_mutex_lockpthread_mutex_lock
  • __gthread_mutex_trylockpthread_mutex_trylock
  • __gthread_mutex_unlockpthread_mutex_unlock

弱符号机制

#define __gthrw(name) __gthrw2(__gthrw_ ## name,name,name)
#define __gthrw2(name,name2,type) \static __typeof(type) name __attribute__ ((__weakref__(#name2)));__gthrw(pthread_mutex_lock)
__gthrw(pthread_mutex_trylock)
__gthrw(pthread_mutex_unlock)

弱符号的优势:

  • 允许在运行时动态链接pthread库
  • 支持在单线程环境下不链接线程库
  • 提供线程库可选性支持

高级mutex类型

1. timed_mutex 实现

template<typename _Derived>
class __timed_mutex_impl
{
protected:typedef chrono::high_resolution_clock __clock_t;template<typename _Rep, typename _Period>bool _M_try_lock_for(const chrono::duration<_Rep, _Period>& __rtime){using chrono::steady_clock;auto __rt = chrono::duration_cast<steady_clock::duration>(__rtime);if (ratio_greater<steady_clock::period, _Period>())++__rt;  // 时钟精度调整return _M_try_lock_until(steady_clock::now() + __rt);}template<typename _Duration>bool _M_try_lock_until(const chrono::time_point<__clock_t, _Duration>& __atime){auto __s = chrono::time_point_cast<chrono::seconds>(__atime);auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);__gthread_time_t __ts = {static_cast<std::time_t>(__s.time_since_epoch().count()),static_cast<long>(__ns.count())};auto __mutex = static_cast<_Derived*>(this)->native_handle();return !__gthread_mutex_timedlock(__mutex, &__ts);}
};

时间处理机制:

  • 支持相对时间(try_lock_for)和绝对时间(try_lock_until)
  • 内部统一转换为绝对时间点处理
  • 处理不同时钟类型和精度转换

2. recursive_timed_mutex

结合了递归mutex和定时mutex的特性:

  • 继承自__recursive_mutex_base__timed_mutex_impl
  • 支持同一线程多次获取
  • 支持超时获取操作

RAII封装器

1. lock_guard

template<typename _Mutex>
class lock_guard
{
public:typedef _Mutex mutex_type;explicit lock_guard(mutex_type& __m) : _M_device(__m){ _M_device.lock(); }lock_guard(mutex_type& __m, adopt_lock_t) : _M_device(__m){ } // 假定已经获取锁~lock_guard(){ _M_device.unlock(); }lock_guard(const lock_guard&) = delete;lock_guard& operator=(const lock_guard&) = delete;private:mutex_type& _M_device;
};

设计特点:

  • 简单的RAII封装,构造时加锁,析构时解锁
  • 不可移动、不可拷贝
  • 支持adopt_lock语义(假定已持有锁)

2. unique_lock

template<typename _Mutex>
class unique_lock
{
public:typedef _Mutex mutex_type;unique_lock() noexcept : _M_device(0), _M_owns(false) { }explicit unique_lock(mutex_type& __m): _M_device(std::__addressof(__m)), _M_owns(false){lock();_M_owns = true;}unique_lock(mutex_type& __m, defer_lock_t) noexcept: _M_device(std::__addressof(__m)), _M_owns(false) { }unique_lock(mutex_type& __m, try_to_lock_t): _M_device(std::__addressof(__m)), _M_owns(_M_device->try_lock()) { }unique_lock(mutex_type& __m, adopt_lock_t): _M_device(std::__addressof(__m)), _M_owns(true) { }~unique_lock(){if (_M_owns)unlock();}// 移动语义支持unique_lock(unique_lock&& __u) noexcept: _M_device(__u._M_device), _M_owns(__u._M_owns){__u._M_device = 0;__u._M_owns = false;}unique_lock& operator=(unique_lock&& __u) noexcept{if(_M_owns)unlock();unique_lock(std::move(__u)).swap(*this);__u._M_device = 0;__u._M_owns = false;return *this;}private:mutex_type* _M_device;bool _M_owns;  // 所有权标记
};

unique_lock优势:

  • 支持延迟锁定(defer_lock)
  • 支持条件锁定(try_to_lock)
  • 支持移动语义,可以传递锁的所有权
  • 灵活的锁定/解锁控制
  • 与条件变量配合使用

多mutex操作

try_lock算法

template<typename _Lock1, typename _Lock2, typename... _Lock3>
int try_lock(_Lock1& __l1, _Lock2& __l2, _Lock3&... __l3)
{int __idx;auto __locks = std::tie(__l1, __l2, __l3...);__try_lock_impl<0>::__do_try_lock(__locks, __idx);return __idx;
}

算法原理:

  • 顺序尝试获取所有mutex
  • 如果某个mutex获取失败,释放已获取的mutex
  • 返回失败的mutex索引,-1表示全部成功

lock算法

template<typename _L1, typename _L2, typename... _L3>
void lock(_L1& __l1, _L2& __l2, _L3&... __l3)
{while (true){unique_lock<_L1> __first(__l1);int __idx;auto __locks = std::tie(__l2, __l3...);__try_locker::__do_try_lock(__locks, __idx);if (__idx == -1){__first.release();return;}}
}

死锁避免策略:

  • 先锁定第一个mutex
  • 尝试获取剩余mutex
  • 如果失败则释放所有已获取的锁,重新尝试
  • 避免了循环等待导致的死锁

call_once实现

once_flag结构

struct once_flag
{
private:typedef __gthread_once_t __native_type;__native_type _M_once = __GTHREAD_ONCE_INIT;public:constexpr once_flag() noexcept = default;once_flag(const once_flag&) = delete;once_flag& operator=(const once_flag&) = delete;
};

call_once实现

template<typename _Callable, typename... _Args>
void call_once(once_flag& __once, _Callable&& __f, _Args&&... __args)
{
#ifdef _GLIBCXX_HAVE_TLSauto __bound_functor = std::__bind_simple(std::forward<_Callable>(__f),std::forward<_Args>(__args)...);__once_callable = std::__addressof(__bound_functor);__once_call = &__once_call_impl<decltype(__bound_functor)>;
#elseunique_lock<mutex> __functor_lock(__get_once_mutex());auto __callable = std::__bind_simple(std::forward<_Callable>(__f),std::forward<_Args>(__args)...);__once_functor = [&]() { __callable(); };__set_once_functor_lock_ptr(&__functor_lock);
#endifint __e = __gthread_once(&__once._M_once, &__once_proxy);if (__e)__throw_system_error(__e);
}

实现机制:

  • TLS版本:使用线程本地存储传递函数对象
  • 非TLS版本:使用全局函数对象和mutex保护
  • 底层依赖pthread_once或平台等价物

性能分析

内存开销

  • std::mutex: 通常sizeof(pthread_mutex_t) ≈ 40字节(x86_64)
  • std::recursive_mutex: 额外存储线程ID和计数器
  • unique_lock: 8字节指针 + 1字节bool (对齐后16字节)

性能特征

  1. 无竞争情况:

    • 现代实现使用futex等快速用户空间操作
    • lock/unlock通常只需几十个CPU周期
  2. 竞争情况:

    • 涉及系统调用和线程调度
    • 性能下降2-3个数量级
  3. try_lock性能:

    • 始终非阻塞,性能稳定
    • 适合乐观锁定场景

最佳实践

1. 选择合适的mutex类型

  • std::mutex: 一般用途,性能最佳
  • std::recursive_mutex: 需要递归锁定时使用,有额外开销
  • std::timed_mutex: 需要超时控制时使用

2. RAII封装器选择

  • lock_guard: 简单场景,自动管理
  • unique_lock: 需要灵活控制或与条件变量配合

3. 避免死锁

// 正确: 使用std::lock避免死锁
std::lock(mutex1, mutex2);
std::lock_guard<std::mutex> lock1(mutex1, std::adopt_lock);
std::lock_guard<std::mutex> lock2(mutex2, std::adopt_lock);// 错误: 可能导致死锁
// mutex1.lock();
// mutex2.lock();

4. 性能优化建议

  • 减少锁的持有时间
  • 考虑读写锁(shared_mutex)替代独占锁
  • 避免在锁内执行I/O操作
  • 合理设计锁粒度

码字不易,欢迎大家点赞,关注,评论,谢谢!

👉 C++训练营

一个专为校招、社招3年工作经验的同学打造的 1v1 项目实战训练营,量身定制学习计划、每日代码review,简历优化,面试辅导,已帮助多名学员获得大厂offer!

http://www.dtcms.com/a/331704.html

相关文章:

  • Xsens动作捕捉与AI驱动人形机器人训练革新
  • WVP和ZLM部署与接入NVR指南环境准备
  • 【React】hooks 中的闭包陷阱
  • 三轴云台之脉宽调制技术篇
  • Qt基本槽
  • 链游(GameFi)开发破局:如何平衡可玩性与经济模型可持续性?
  • GraphRAG:AI理解复杂知识的未知领域,开启探索之旅
  • 《Python函数:从入门到精通,一文掌握函数编程精髓》
  • MySQL主从原理
  • Linux 文件系统简介
  • 解析 TrueType/OpenType 格式的可变字体(Variable Font),提取其所有命名实例(Named Instances) 的名称信息
  • ESP32S3的LVGL配置参数解释、动画播放优化(更新中)
  • 4.1vue3的setup()
  • 《WebGL中FBO的底层运行逻辑》
  • 编程与数学 02-017 Python 面向对象编程 01课题、面向对象
  • 【会员专享数据】2000-2024年我国乡镇的逐日PM₁₀数据(Shp/Excel格式)
  • linux初始化配置
  • 计算机网络知识
  • 基于Java飞算AI的Spring Boot聊天室系统全流程实战
  • 【奔跑吧!Linux 内核(第二版)】第6章:简单的字符设备驱动(三)
  • CMake include_directories()使用指南
  • 从零开始的云计算生活——第四十三天,激流勇进,kubernetes模块之Pod资源对象
  • 莫队 Little Elephant and Array
  • GUI Grounding: ScreenSpot
  • 力扣-62.不同路径
  • AM原理与配置
  • 【网络安全测试】手机APP安全测试工具NowSecure 使用指导手册(有关必回)
  • Monsters
  • Redis7学习--持久化机制 RDB与AOF
  • 【SLAM】不同相机模型及其常见的链式求导推导