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_lock
→pthread_mutex_lock
__gthread_mutex_trylock
→pthread_mutex_trylock
__gthread_mutex_unlock
→pthread_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字节)
性能特征
-
无竞争情况:
- 现代实现使用futex等快速用户空间操作
- lock/unlock通常只需几十个CPU周期
-
竞争情况:
- 涉及系统调用和线程调度
- 性能下降2-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!