std::mutex/std::lock_guard/std::unique_lock/std::scoped_lock/std::recursive_mutex等族函数简单梳理
date
author
gcc
kernel
glibc
20220331
river
gcc-11.2.0
linux-4.18.1
glibc-2.35
class
since
file
meaning
std::mutex
C++11
std_mutex.h
pthread_mutex_t的封装
std::lock_guard
C++11
std_mutex.h
简单的mutex的守卫类,没有对外接口,支持std::adopt_lock
std::unique_lock
C++11
unique_lock.h
mutex的守卫类,有对外接口,支持std::adopt_lock/std::defer_lock/std::try_to_lock
std::scoped_lock
C++17
mutex
支持对多个mutex的加锁,内部依赖std::lock(…)
std::recursive_mutex
C++11
mutex
递归锁,通过pthread_mutex_t的attr中参数实现递归
std::adopt_lock
C++17
std_mutex.h
不执行lock(),但保存mutex的地址/引用
std::defer_lock
C++11
std_mutex.h
不执行lock(),也不own
std::try_to_lock
C++11
std_mutex.h
通过pthread_mutex_trylock的结果决定是否own
std::lock
C++11
mutex
依赖c++17的模板推导机制,支持锁多个lockable object
std::mutex #include <gcc-11.2.0/libstdc++-v3/include/bits/std_mutex.h>
datastruct mutex::__mutex_base::_M_mutex即是pthread_mutex_t.
lock() 1 2 3 4 5 6 7 8 9 void lock () { int __e = __gthread_mutex_lock(&_M_mutex); if (__e) __throw_system_error(__e); }
对应pthread_mutex_lock,失败抛异常.
try_lock() 1 2 3 4 5 6 bool try_lock () noexcept { return !__gthread_mutex_trylock(&_M_mutex); }
对应pthread_mutex_trylock,不抛异常.
unlock() 1 2 3 4 5 6 void unlock () { __gthread_mutex_unlock(&_M_mutex); }
对应pthread_mutex_unlock.
native_handle() 1 2 3 native_handle_type native_handle () noexcept { return &_M_mutex; }
返回pthread_mutex_t.
std::lock_guard 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 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 ) noexcept : _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; };
非常简单的一个守卫类。传的是一个”mutex”的引用, 没有提供对外的函数。如果带std::adopt_lock初始化,则只引用不加锁,如下:
1 std::lock_guard<std::mutex> lock(m,std::adopt_lock);
构造函数必须传入一个”mutex”的左值。
1 2 3 4 mutex m; lock_guard<mutex> l1; // err lock_guard<mutex> l2(); // 成函数声明了 lock_guard<mutex> l3(m); // ok
unique_lock 相比lock_guard,多了一堆外部接口。多了一个own成员表示是否持有,自由度更高,支持defer_lock
def 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 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 ) noexcept : _M_device(std::__addressof(__m)), _M_owns(true ) { } template <typename _Clock, typename _Duration> unique_lock (mutex_type& __m, const chrono::time_point<_Clock, _Duration>& __atime) : _M_device(std::__addressof(__m)), _M_owns(_M_device->try_lock_until (__atime)) { } template <typename _Rep, typename _Period> unique_lock (mutex_type& __m, const chrono::duration<_Rep, _Period>& __rtime) : _M_device(std::__addressof(__m)), _M_owns(_M_device->try_lock_for (__rtime)) { } ~unique_lock () { if (_M_owns) unlock (); } unique_lock (const unique_lock&) = delete ; unique_lock& operator =(const unique_lock&) = delete ; 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 ; } explicit operator bool () const noexcept { return owns_lock (); } private : mutex_type* _M_device; bool _M_owns; };
lock() 1 2 3 4 5 6 7 8 9 10 11 12 13 void lock () { if (!_M_device) __throw_system_error(int (errc::operation_not_permitted)); else if (_M_owns) __throw_system_error(int (errc::resource_deadlock_would_occur)); else { _M_device->lock (); _M_owns = true ; } }
unique_lock构造函数可以不传”mutex”进来,所以分3种情况,最终调用posix的lock().
没关联Mutex
没hold mutex
hold mutex
try_lock() 1 2 3 4 5 6 7 8 9 10 11 12 13 bool try_lock () { if (!_M_device) __throw_system_error(int (errc::operation_not_permitted)); else if (_M_owns) __throw_system_error(int (errc::resource_deadlock_would_occur)); else { _M_owns = _M_device->try_lock (); return _M_owns; } }
也是3种情况,和lock()差不多,调用posix的try_lock().
try_lock_for()
attempts to lock (i.e., takes ownership of) the associated TimedLockable mutex, returns if the mutex has been unavailable for the specified time duration (public member function)
支持不同时间格式的timeout来try_lock,底层是posix的pthread_mutex_timedlock/pthread_mutex_clocklock
try_lock_until
tries to lock (i.e., takes ownership of) the associated TimedLockable mutex, returns if the mutex has been unavailable until specified time point has been reached (public member function)
支持不同时间格式的timeout来try_lock,底层是posix的pthread_mutex_timedlock/pthread_mutex_clocklock
unlock() 1 2 3 4 5 6 7 8 9 10 11 void unlock () { if (!_M_owns) __throw_system_error(int (errc::operation_not_permitted)); else if (_M_device) { _M_device->unlock (); _M_owns = false ; } }
未拥有就unlock会异常
swap()
swaps state with another std::unique_lock(public member function)
release()
disassociates the associated mutex without unlocking (i.e., releasing ownership of) it (public member function)
1 2 3 4 5 6 7 8 mutex_type* release () noexcept { mutex_type* __ret = _M_device; _M_device = 0 ; _M_owns = false ; return __ret; }
释放所有权,状态置为空,返回原来的mutex的地址。
mutex()
returns a pointer to the associated mutex (public member function)
1 2 3 mutex_type* mutex () const noexcept { return _M_device; }
返回锁的地址
owns_lock
tests whether the lock owns (i.e., has locked) its associated mutex (public member function)
1 2 3 bool owns_lock () const noexcept { return _M_owns; }
scoped_lock 代码不多,放上来了。scoped_lock只接收一个参数的话和lock_guard看起来没啥区别,主要是对于多个参数的使用比较好,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 { std::mutex m1, m2; std::lock (m1, m2); std::lock_guard<std::mutex> l1 (m1, std::adopt_lock) ; std::lock_guard<std::mutex> l2 (m2, std::adopt_lock) ; } { std::mutex m1, m2; std::scoped_lock<std::mutex, std::mutex> sl(m1, m2); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 #if __cplusplus >= 201703L #define __cpp_lib_scoped_lock 201703 template <typename ... _MutexTypes> class scoped_lock { public : explicit scoped_lock (_MutexTypes&... __m) : _M_devices(std::tie(__m...)) { std::lock (__m...); } explicit scoped_lock (adopt_lock_t , _MutexTypes&... __m) noexcept : _M_devices(std::tie(__m...)) { } ~scoped_lock () { std::apply ([](auto &... __m) { (__m.unlock (), ...); }, _M_devices); } scoped_lock (const scoped_lock&) = delete ; scoped_lock& operator =(const scoped_lock&) = delete ; private : tuple<_MutexTypes&...> _M_devices; }; template <> class scoped_lock < > { public : explicit scoped_lock () = default ; explicit scoped_lock (adopt_lock_t ) noexcept { } ~scoped_lock () = default ; scoped_lock (const scoped_lock&) = delete ; scoped_lock& operator =(const scoped_lock&) = delete ; }; template <typename _Mutex> class scoped_lock < _Mutex> { public : using mutex_type = _Mutex; explicit scoped_lock (mutex_type& __m) : _M_device(__m) { _M_device.lock (); } explicit scoped_lock (adopt_lock_t , mutex_type& __m) noexcept : _M_device(__m) { } ~scoped_lock () { _M_device.unlock (); } scoped_lock (const scoped_lock&) = delete ; scoped_lock& operator =(const scoped_lock&) = delete ; private : mutex_type& _M_device; }; #endif
recursive_mutex
线程对已经获取的 std::mutex (已经上锁)再次上锁是错误的,尝试这样做会导致未定义行为。在某些情况下,一个线程会尝试在释放一个互斥量前多次获取。因此,C++标准库提供了 std::recursive_mutex 类。
recursive_mutex::__recursive_mutex_base::_M_mutex就是pthread_mutex_t. recursive_mutex和mutex的区别主要在构造函数中,给pthread_mutex_t加了attr(PTHREAD_MUTEX_RECURSIVE ):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 static inline int __gthread_recursive_mutex_init_function (__gthread_recursive_mutex_t *__mutex) { if (__gthread_active_p ()) { pthread_mutexattr_t __attr; int __r; __r = __gthrw_(pthread_mutexattr_init) (&__attr); if (!__r) __r = __gthrw_(pthread_mutexattr_settype) (&__attr, PTHREAD_MUTEX_RECURSIVE); if (!__r) __r = __gthrw_(pthread_mutex_init) (__mutex, &__attr); if (!__r) __r = __gthrw_(pthread_mutexattr_destroy) (&__attr); return __r; } return 0 ; }
PTHREAD_MUTEX_RECURSIVE在posix下的实现,就是保存了tid+count,来维持一个引用计数,如下:
lock 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 else if (__builtin_expect (PTHREAD_MUTEX_TYPE (mutex) == PTHREAD_MUTEX_RECURSIVE_NP, 1 )) { pid_t id = THREAD_GETMEM (THREAD_SELF, tid); if (mutex->__data.__owner == id) { if (__glibc_unlikely (mutex->__data.__count + 1 == 0 )) return EAGAIN; ++mutex->__data.__count; return 0 ; } LLL_MUTEX_LOCK_OPTIMIZED (mutex); assert (mutex->__data.__owner == 0 ); mutex->__data.__count = 1 ; }
unlock 1 2 3 4 5 6 7 8 9 10 11 12 else if (__builtin_expect (PTHREAD_MUTEX_TYPE (mutex) == PTHREAD_MUTEX_RECURSIVE_NP, 1 )) { if (mutex->__data.__owner != THREAD_GETMEM (THREAD_SELF, tid)) return EPERM; if (--mutex->__data.__count != 0 ) return 0 ; goto normal; }
std::lock std::lock对可变参数的对象进行加锁,实现上不放代码了,简单地说:
通过unique_lock来加锁
通过unique_lock::release释放所有权,不解锁
通过模板递归实例化来处理…args
reference https://en.cppreference.com/w/cpp/thread/mutex