zoukankan      html  css  js  c++  java
  • C++11中的mutex, lock,condition variable实现分析

    本文分析的是llvm libc++的实现:http://libcxx.llvm.org/

    C++11中的各种mutex, lock对象,实际上都是对posix的mutex,condition的封装。不过里面也有很多细节值得学习。

    std::mutex

    先来看下std::mutex:

    包增了一个pthread_mutex_t __m_,很简单,每个函数该干嘛就干嘛。

    [cpp] view plain copy
     
    1. class mutex  
    2. {  
    3.     pthread_mutex_t __m_;  
    4.   
    5. public:  
    6.      mutex() _NOEXCEPT {__m_ = (pthread_mutex_t)<strong>PTHREAD_MUTEX_INITIALIZER</strong>;}  
    7.      ~mutex();  
    8. private:  
    9.     mutex(const mutex&);// = delete;  
    10.     mutex& operator=(const mutex&);// = delete;  
    11. public:  
    12.     void lock();  
    13.     bool try_lock() _NOEXCEPT;  
    14.     void unlock() _NOEXCEPT;  
    15.   
    16.     typedef pthread_mutex_t* native_handle_type;  
    17.     _LIBCPP_INLINE_VISIBILITY native_handle_type native_handle() {return &__m_;}  
    18. };  
    19.   
    20. mutex::~mutex()  
    21. {  
    22.     pthread_mutex_destroy(&__m_);  
    23. }  
    24.   
    25. void mutex::lock()  
    26. {  
    27.     int ec = pthread_mutex_lock(&__m_);  
    28.     if (ec)  
    29.         __throw_system_error(ec, "mutex lock failed");  
    30. }  
    31.   
    32. bool mutex::try_lock() _NOEXCEPT  
    33. {  
    34.     return pthread_mutex_trylock(&__m_) == 0;  
    35. }  
    36.   
    37. void mutex::unlock() _NOEXCEPT  
    38. {  
    39.     int ec = pthread_mutex_unlock(&__m_);  
    40.     (void)ec;  
    41.     assert(ec == 0);  
    42. }  

    三种锁状态:std::defer_lock, std::try_to_lock, std::adopt_lock

    这三个是用于标识锁在传递到一些包装类时,锁的状态:
    std::defer_lock,还没有获取到锁
    std::try_to_lock,在包装类构造时,尝试去获取锁
    std::adopt_lock,调用者已经获得了锁
    这三个东东,实际上是用于偏特化的,是三个空的struct:
    [cpp] view plain copy
     
    1. struct  defer_lock_t {};  
    2. struct  try_to_lock_t {};  
    3. struct  adopt_lock_t {};  
    4. constexpr defer_lock_t  defer_lock  = defer_lock_t();  
    5. constexpr try_to_lock_t try_to_lock = try_to_lock_t();  
    6. constexpr adopt_lock_t  adopt_lock  = adopt_lock_t();  
    在下面的代码里,就可以看到这三个东东是怎么用的了。

    std::lock_guard

    这个类比较重要,因为我们真正使用lock的时候,大部分都是要用这个。

    这个类其实很简单:

    在构造函数里调用 mutext.lock(),
    在释构函数里,调用了mutex.unlock() 函数。

    因为C++会在函数抛出异常时,自动调用作用域内的变量的析构函数,所以使用std::lock_guard可以在异常时自动释放锁,这就是为什么要避免直接使用mutex的函数,而是要用std::lock_guard的原因了。

    [cpp] view plain copy
     
    1. template <class _Mutex>  
    2. class lock_guard  
    3. {  
    4. public:  
    5.     typedef _Mutex mutex_type;  
    6. private:  
    7.     mutex_type& __m_;  
    8. public:  
    9.     explicit lock_guard(mutex_type& __m)  
    10.         : __m_(__m) {__m_.lock();}  
    11.     lock_guard(mutex_type& __m, adopt_lock_t)  
    12.         : __m_(__m) {}  
    13.     ~lock_guard() {__m_.unlock();}  
    14. private:  
    15.     lock_guard(lock_guard const&);// = delete;  
    16.     lock_guard& operator=(lock_guard const&);// = delete;  
    17. };  

    注意,std::lock_guard的两个构造函数,当只传递mutex时,会在构造函数时调用mutext.lock()来获得锁。

    当传递了adopt_lock_t时,说明调用者已经拿到了锁,所以不再尝试去获得锁。

    std::unique_lock

    unique_lock实际上也是一个包装类,起名为unique可能是和std::lock函数区分用的。
    注意,多了一个owns_lock函数和release()函数,这两个在std::lock函数会用到。

    owns_lock函数用于判断是否拥有锁;

    release()函数则放弃了对锁的关联,当析构时,不会去unlock锁。
    再看下unique_lock的实现,可以发现,上面的三种类型就是用来做偏特化用的:

    [cpp] view plain copy
     
    1. template <class _Mutex>  
    2. class unique_lock  
    3. {  
    4. public:  
    5.     typedef _Mutex mutex_type;  
    6. private:  
    7.     mutex_type* __m_;  
    8.     bool __owns_;  
    9.   
    10. public:  
    11.     unique_lock() _NOEXCEPT : __m_(nullptr), __owns_(false) {}  
    12.     explicit unique_lock(mutex_type& __m)  
    13.         : __m_(&__m), __owns_(true) {__m_->lock();}  
    14.     unique_lock(mutex_type& __m, defer_lock_t) _NOEXCEPT  
    15.         : __m_(&__m), __owns_(false) {}  
    16.     unique_lock(mutex_type& __m, try_to_lock_t)    //偏特化  
    17.         : __m_(&__m), __owns_(__m.try_lock()) {}  
    18.     unique_lock(mutex_type& __m, adopt_lock_t)     //偏特化  
    19.         : __m_(&__m), __owns_(true) {}  
    20.     template <class _Clock, class _Duration>  
    21.         unique_lock(mutex_type& __m, const chrono::time_point<_Clock, _Duration>& __t)  
    22.             : __m_(&__m), __owns_(__m.try_lock_until(__t)) {}  
    23.     template <class _Rep, class _Period>  
    24.         unique_lock(mutex_type& __m, const chrono::duration<_Rep, _Period>& __d)  
    25.             : __m_(&__m), __owns_(__m.try_lock_for(__d)) {}  
    26.     ~unique_lock()  
    27.     {  
    28.         if (__owns_)  
    29.             __m_->unlock();  
    30.     }  
    31.   
    32. private:  
    33.     unique_lock(unique_lock const&); // = delete;  
    34.     unique_lock& operator=(unique_lock const&); // = delete;  
    35.   
    36. public:  
    37.     unique_lock(unique_lock&& __u) _NOEXCEPT  
    38.         : __m_(__u.__m_), __owns_(__u.__owns_)  
    39.         {__u.__m_ = nullptr; __u.__owns_ = false;}  
    40.     unique_lock& operator=(unique_lock&& __u) _NOEXCEPT  
    41.         {  
    42.             if (__owns_)  
    43.                 __m_->unlock();  
    44.             __m_ = __u.__m_;  
    45.             __owns_ = __u.__owns_;  
    46.             __u.__m_ = nullptr;  
    47.             __u.__owns_ = false;  
    48.             return *this;  
    49.         }  
    50.   
    51.     void lock();  
    52.     bool try_lock();  
    53.   
    54.     template <class _Rep, class _Period>  
    55.     bool try_lock_for(const chrono::duration<_Rep, _Period>& __d);  
    56.     template <class _Clock, class _Duration>  
    57.     bool try_lock_until(const chrono::time_point<_Clock, _Duration>& __t);  
    58.   
    59.     void unlock();  
    60.     void swap(unique_lock& __u) _NOEXCEPT  
    61.     {  
    62.         _VSTD::swap(__m_, __u.__m_);  
    63.         _VSTD::swap(__owns_, __u.__owns_);  
    64.     }  
    65.     mutex_type* release() _NOEXCEPT  
    66.     {  
    67.         mutex_type* __m = __m_;  
    68.         __m_ = nullptr;  
    69.         __owns_ = false;  
    70.         return __m;  
    71.     }  
    72.     bool owns_lock() const _NOEXCEPT {return __owns_;}  
    73.     operator bool () const _NOEXCEPT {return __owns_;}  
    74.     mutex_type* mutex() const _NOEXCEPT {return __m_;}  
    75. };  

    std::lock和std::try_lock函数

    上面的都是类对象,这两个是函数。

    std::lock和std::try_lock函数用于在同时使用多个锁时,防止死锁。这个实际上很重要的,因为手写代码来处理多个锁的同步问题,很容易出错。

    要注意的是std::try_lock函数的返回值:

    当成功时,返回-1;

    当失败时,返回第几个锁没有获取成功,以0开始计数;

    首先来看下只有两个锁的情况,代码虽然看起来比较简单,但里面却有大文章:

    [cpp] view plain copy
     
    1. template <class _L0, class _L1>  
    2. void  
    3. lock(_L0& __l0, _L1& __l1)  
    4. {  
    5.     while (true)  
    6.     {  
    7.         {  
    8.             unique_lock<_L0> __u0(__l0);  
    9.             if (__l1.try_lock())  //已获得锁l0,再尝试获取l1  
    10.             {  
    11.                 __u0.release();   //l0和l1都已获取到,因为unique_lock在释构时会释放l0,所以要调用release()函数,不让它释放l0锁。  
    12.                 break;  
    13.             }  
    14.         }//如果同时获取l0,l1失败,这里会释放l0。  
    15.         sched_yield();  //把线程放到同一优先级的调度队列的尾部,CPU切换到其它线程执行  
    16.         {  
    17.             unique_lock<_L1> __u1(__l1); //因为上面尝试先获取l1失败,说明有别的线程在持有l1,那么这次先尝试获取锁l1(只有前面的线程释放了,才可能获取到)  
    18.             if (__l0.try_lock())  
    19.             {  
    20.                 __u1.release();  
    21.                 break;  
    22.             }  
    23.         }  
    24.         sched_yield();  
    25.     }  
    26. }  
    27. template <class _L0, class _L1>  
    28. int  
    29. try_lock(_L0& __l0, _L1& __l1)  
    30. {  
    31.     unique_lock<_L0> __u0(__l0, try_to_lock);  
    32.     if (__u0.owns_lock())  
    33.     {  
    34.         if (__l1.try_lock()) //注意try_lock返回值的定义,否则这里无法理解  
    35.         {  
    36.             __u0.release();  
    37.             return -1;  
    38.         }  
    39.         else  
    40.             return 1;  
    41.     }  
    42.     return 0;  
    43. }  

    上面的lock函数用尝试的办法防止了死锁。

    上面是两个锁的情况,那么在多个参数的情况下呢?

    先来看下std::try_lock函数的实现:

    里面递归地调用了try_lock函数自身,如果全部锁都获取成功,则依次把所有的unique_lock都release掉。

    如果有失败,则计数失败的次数,最终返回。

    [cpp] view plain copy
     
    1. template <class _L0, class _L1, class _L2, class... _L3>  
    2. int  
    3. try_lock(_L0& __l0, _L1& __l1, _L2& __l2, _L3&... __l3)  
    4. {  
    5.     int __r = 0;  
    6.     unique_lock<_L0> __u0(__l0, try_to_lock);  
    7.     if (__u0.owns_lock())  
    8.     {  
    9.         __r = try_lock(__l1, __l2, __l3...);  
    10.         if (__r == -1)  
    11.             __u0.release();  
    12.         else  
    13.             ++__r;  
    14.     }  
    15.     return __r;  
    16. }  

    再来看多参数的std::lock的实现:

    [cpp] view plain copy
     
    1. template <class _L0, class _L1, class _L2, class ..._L3>  
    2. void  
    3. __lock_first(int __i, _L0& __l0, _L1& __l1, _L2& __l2, _L3& ...__l3)  
    4. {  
    5.     while (true)  
    6.     {  
    7.         switch (__i)  //__i用来标记上一次获取参数里的第几个锁失败,从0开始计数  
    8.         {  
    9.         case 0:   //第一次执行时,__i是0  
    10.             {  
    11.                 unique_lock<_L0> __u0(__l0);  
    12.                 __i = try_lock(__l1, __l2, __l3...);  
    13.                 if (__i == -1)  //获取到l0之后,如果尝试获取后面的锁也成功了,即全部锁都获取到了,则设置unique_lock为release,并返回  
    14.                 {  
    15.                     __u0.release();  
    16.                     return;  
    17.                 }  
    18.             }  
    19.             ++__i;  //因为__i表示是获取第几个锁失败,而上面的try_lock(__l1,__l2__l3,...)是从l1开始的,因此这里要+1,调整到没有获取成功的锁上,下次先从它开始获取。  
    20.             sched_yield();  
    21.             break;  
    22.         case 1:   //说明上次获取l1失败,这次先获取到l1。  
    23.             {  
    24.                 unique_lock<_L1> __u1(__l1);      
    25.                 __i = try_lock(__l2, __l3..., __l0);   //把前一次的l0放到最后。这次先获取到了l1,再尝试获取后面的锁。  
    26.                 if (__i == -1)  
    27.                 {  
    28.                     __u1.release();  
    29.                     return;  
    30.                 }  
    31.             }  
    32.             if (__i == sizeof...(_L3) + 1)   //说明把l0放到最后面时,最后获取l0时失败了。那么说明现在有其它线程持有l0,那么下一次要从l0开始获取。  
    33.                 __i = 0;  
    34.             else  
    35.                 __i += 2; //因为__i表示是获取第几个锁失败,而上面的try_lock(__l2,__l3..., __l0)是从l2开始的,因此这里要+2  
    36.             sched_yield();  
    37.             break;  
    38.         default:  
    39.             __lock_first(__i - 2, __l2, __l3..., __l0, __l1);    //因为这里是从l2开始的,因此__i要减2。  
    40.             return;  
    41.         }  
    42.     }  
    43. }  
    44.   
    45. template <class _L0, class _L1, class _L2, class ..._L3>  
    46. inline _LIBCPP_INLINE_VISIBILITY  
    47. void  
    48. lock(_L0& __l0, _L1& __l1, _L2& __l2, _L3& ...__l3)  
    49. {  
    50.     __lock_first(0, __l0, __l1, __l2, __l3...);  
    51. }  

    可以看到多参数的std::lock的实现是:

    先获取一个锁,然后再调用std::try_lock去获取剩下的锁,如果失败了,则下次先获取上次失败的锁。

    重复上面的过程,直到成功获取到所有的锁。

    上面的算法用比较巧妙的方式实现了参数的轮转。

    std::timed_mutex

    std::timed_mutex   是里面封装了mutex和condition,这样就两个函数可以用:
    try_lock_for
    try_lock_until 

    实际上是posix的mutex和condition的包装。

    [cpp] view plain copy
     
    1. class timed_mutex  
    2. {  
    3.     mutex              __m_;  
    4.     condition_variable __cv_;  
    5.     bool               __locked_;  
    6. public:  
    7.      timed_mutex();  
    8.      ~timed_mutex();  
    9. private:  
    10.     timed_mutex(const timed_mutex&); // = delete;  
    11.     timed_mutex& operator=(const timed_mutex&); // = delete;  
    12. public:  
    13.     void lock();  
    14.     bool try_lock() _NOEXCEPT;  
    15.     template <class _Rep, class _Period>  
    16.         _LIBCPP_INLINE_VISIBILITY  
    17.         bool try_lock_for(const chrono::duration<_Rep, _Period>& __d)  
    18.             {return try_lock_until(chrono::steady_clock::now() + __d);}  
    19.     template <class _Clock, class _Duration>  
    20.         bool try_lock_until(const chrono::time_point<_Clock, _Duration>& __t);  
    21.     void unlock() _NOEXCEPT;  
    22. };  
    23.   
    24. template <class _Clock, class _Duration>  
    25. bool  
    26. timed_mutex::try_lock_until(const chrono::time_point<_Clock, _Duration>& __t)  
    27. {  
    28.     using namespace chrono;  
    29.     unique_lock<mutex> __lk(__m_);  
    30.     bool no_timeout = _Clock::now() < __t;  
    31.     while (no_timeout && __locked_)  
    32.         no_timeout = __cv_.wait_until(__lk, __t) == cv_status::no_timeout;  
    33.     if (!__locked_)  
    34.     {  
    35.         __locked_ = true;  
    36.         return true;  
    37.     }  
    38.     return false;  
    39. }  

    std::recursive_mutex和std::recursive_timed_mutex

    这两个实际上是std::mutex和std::timed_mutex 的recursive模式的实现,即锁得获得者可以重复多次调用lock()函数。

    和posix mutex里的recursive mutex是一样的。

    看下std::recursive_mutex的构造函数就知道了。

    [cpp] view plain copy
     
    1. recursive_mutex::recursive_mutex()  
    2. {  
    3.     pthread_mutexattr_t attr;  
    4.     int ec = pthread_mutexattr_init(&attr);  
    5.     if (ec)  
    6.         goto fail;  
    7.     ec = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);  
    8.     if (ec)  
    9.     {  
    10.         pthread_mutexattr_destroy(&attr);  
    11.         goto fail;  
    12.     }  
    13.     ec = pthread_mutex_init(&__m_, &attr);  
    14.     if (ec)  
    15.     {  
    16.         pthread_mutexattr_destroy(&attr);  
    17.         goto fail;  
    18.     }  
    19.     ec = pthread_mutexattr_destroy(&attr);  
    20.     if (ec)  
    21.     {  
    22.         pthread_mutex_destroy(&__m_);  
    23.         goto fail;  
    24.     }  
    25.     return;  
    26. fail:  
    27.     __throw_system_error(ec, "recursive_mutex constructor failed");  
    28. }  

    std::cv_status

    这个用来表示condition等待返回的状态的,和上面的三个表示lock的状态的用途差不多。

    [cpp] view plain copy
     
    1. enum cv_status  
    2. {  
    3.     no_timeout,  
    4.     timeout  
    5. };  

    std::condition_variable

    包装了posix condition variable。

    [cpp] view plain copy
     
    1. class condition_variable  
    2. {  
    3.     pthread_cond_t __cv_;  
    4. public:  
    5.     condition_variable() {__cv_ = (pthread_cond_t)PTHREAD_COND_INITIALIZER;}  
    6.     ~condition_variable();  
    7. private:  
    8.     condition_variable(const condition_variable&); // = delete;  
    9.     condition_variable& operator=(const condition_variable&); // = delete;  
    10. public:  
    11.     void notify_one() _NOEXCEPT;  
    12.     void notify_all() _NOEXCEPT;  
    13.   
    14.     void wait(unique_lock<mutex>& __lk) _NOEXCEPT;  
    15.     template <class _Predicate>  
    16.         void wait(unique_lock<mutex>& __lk, _Predicate __pred);  
    17.   
    18.     template <class _Clock, class _Duration>  
    19.         cv_status  
    20.         wait_until(unique_lock<mutex>& __lk,  
    21.                    const chrono::time_point<_Clock, _Duration>& __t);  
    22.   
    23.     template <class _Clock, class _Duration, class _Predicate>  
    24.         bool  
    25.         wait_until(unique_lock<mutex>& __lk,  
    26.                    const chrono::time_point<_Clock, _Duration>& __t,  
    27.                    _Predicate __pred);  
    28.   
    29.     template <class _Rep, class _Period>  
    30.         cv_status  
    31.         wait_for(unique_lock<mutex>& __lk,  
    32.                  const chrono::duration<_Rep, _Period>& __d);  
    33.   
    34.     template <class _Rep, class _Period, class _Predicate>  
    35.         bool  
    36.         wait_for(unique_lock<mutex>& __lk,  
    37.                  const chrono::duration<_Rep, _Period>& __d,  
    38.                  _Predicate __pred);  
    39.   
    40.     typedef pthread_cond_t* native_handle_type;  
    41.     _LIBCPP_INLINE_VISIBILITY native_handle_type native_handle() {return &__cv_;}  
    42.   
    43. private:  
    44.     void __do_timed_wait(unique_lock<mutex>& __lk,  
    45.        chrono::time_point<chrono::system_clock, chrono::nanoseconds>) _NOEXCEPT;  
    46. };  


    里面的函数都是符合直觉的实现,值得注意的是:

    cv_status是通过判断时间而确定的,如果超时的则返回cv_status::timeout,如果没有超时,则返回cv_status::no_timeout。

    condition_variable::wait_until函数可以传入一个predicate,即一个用户自定义的判断是否符合条件的函数。这个也是很常见的模板编程的方法了。

    [cpp] view plain copy
     
    1. template <class _Clock, class _Duration>  
    2. cv_status  
    3. condition_variable::wait_until(unique_lock<mutex>& __lk,  
    4.                                const chrono::time_point<_Clock, _Duration>& __t)  
    5. {  
    6.     using namespace chrono;  
    7.     wait_for(__lk, __t - _Clock::now());  
    8.     return _Clock::now() < __t ? cv_status::no_timeout : cv_status::timeout;  
    9. }  
    10.   
    11. template <class _Clock, class _Duration, class _Predicate>  
    12. bool  
    13. condition_variable::wait_until(unique_lock<mutex>& __lk,  
    14.                    const chrono::time_point<_Clock, _Duration>& __t,  
    15.                    _Predicate __pred)  
    16. {  
    17.     while (!__pred())  
    18.     {  
    19.         if (wait_until(__lk, __t) == cv_status::timeout)  
    20.             return __pred();  
    21.     }  
    22.     return true;  
    23. }  

    std::condition_variable_any

    std::condition_variable_any的接口和std::condition_variable一样,不同的是std::condition_variable只能使用std::unique_lock<std::mutex>,而std::condition_variable_any可以使用任何的锁对象。

    下面来看下为什么std::condition_variable_any可以使用任意的锁对象。

    [cpp] view plain copy
     
    1. class _LIBCPP_TYPE_VIS condition_variable_any  
    2. {  
    3.     condition_variable __cv_;  
    4.     shared_ptr<mutex>  __mut_;  
    5. public:  
    6.     condition_variable_any();  
    7.   
    8.     void notify_one() _NOEXCEPT;  
    9.     void notify_all() _NOEXCEPT;  
    10.   
    11.     template <class _Lock>  
    12.         void wait(_Lock& __lock);  
    13.     template <class _Lock, class _Predicate>  
    14.         void wait(_Lock& __lock, _Predicate __pred);  
    15.   
    16.     template <class _Lock, class _Clock, class _Duration>  
    17.         cv_status  
    18.         wait_until(_Lock& __lock,  
    19.                    const chrono::time_point<_Clock, _Duration>& __t);  
    20.   
    21.     template <class _Lock, class _Clock, class _Duration, class _Predicate>  
    22.         bool  
    23.         wait_until(_Lock& __lock,  
    24.                    const chrono::time_point<_Clock, _Duration>& __t,  
    25.                    _Predicate __pred);  
    26.   
    27.     template <class _Lock, class _Rep, class _Period>  
    28.         cv_status  
    29.         wait_for(_Lock& __lock,  
    30.                  const chrono::duration<_Rep, _Period>& __d);  
    31.   
    32.     template <class _Lock, class _Rep, class _Period, class _Predicate>  
    33.         bool  
    34.         wait_for(_Lock& __lock,  
    35.                  const chrono::duration<_Rep, _Period>& __d,  
    36.                  _Predicate __pred);  
    37. };  

    可以看到,在std::condition_variable_any里,用shared_ptr<mutex>  __mut_来包装了mutex。所以一切都明白了,回顾std::unique_lock<std::mutex>,它包装了mutex,当析构时自动释放mutex。在std::condition_variable_any里,这份工作让shared_ptr<mutex>来做了。

    因此,也可以很轻松得出std::condition_variable_any会比std::condition_variable稍慢的结论了。

    其它的东东:

    sched_yield()函数的man手册:
    sched_yield() causes the calling thread to relinquish the CPU.  The thread is moved to the end of the queue for its
           static priority and a new thread gets to run.  

    在C++14里还有std::shared_lock和std::shared_timed_mutex,但是libc++里还没有对应的实现,因此不做分析。

    总结

    llvm libc++中的各种mutex, lock, condition variable实际上是封闭了posix里的对应实现。封装的技巧和一些细节值得细细推敲学习。

    看完了实现源码之后,对于如何使用就更加清晰了。

    参考:

    http://en.cppreference.com/w/cpp

    http://libcxx.llvm.org/

    互斥锁有可重入、不可重入之分。C++标准库中用mutex表示不可重入的互斥锁,用recursive_mutex表示可重入的互斥锁。为这两个类增加根据时间来阻塞线程的能力,就又有了两个新的互斥锁:timed_mutex(不可重入的锁)、recursive_timed_mutex(可重入的锁)。

    互斥锁单独使用时主要是为了使对共享资源的互斥使用,即同时只能有一个线程使用,以防止同时使用可能造成的数据问题。

    C++标准库的所有mutex都是不可拷贝的,也不可移动。

    mutex基本操作

    上锁 lock 如果mutex未上锁,则将其上锁。否则如果已经其它线程lock,则阻塞当前线程。

    上锁 try_lock 如果mutex未上锁,则将其上锁。否则返回false,并不阻塞当前线程。

    解锁 unlock  如果mutex被当前线程锁住,则将其解锁。否则,是未定义的行为。

    timed_mutex在mutex的基础上增加了以下两个操作

    try_lock_for(duration) 如果timed_mutex未上锁,则将其上锁,否则阻塞当前线程,但最长只阻塞duration表示的时间段。

    try_lock_until(time_point) 如果timed_mutex未上锁,则将其上锁,否则阻塞当前线程,但最长只会阻塞到time_point表示的时间点就不再阻塞。

    try_lock_for/until可以检测到死锁的出现,这是目前想到的一种用途。

    if(!try_lock_for(chrono::hours(1)))
    {
      throw "出现死锁!";  
    }

    可重入的锁 recursive_mutex、recursive_timed_mutex与对应的mutex、timed_mutex操作一致。不同点在于,不可重入的锁在lock或try_lock一个已经被当前线程lock的锁时会导致死锁,而可重入的锁不会。

    辅助类

    template<class Mutex> class lock_guard;

    lock_guard用于脱离lock_guard对象生存期后自动对互斥锁进行解锁操作。

    explicit lock_guard(mutex_type &m);对象创建时执行 m.lock(),对象销毁时执行 m.unlock()

    explicit lock_guard(mutex_type &m,adpot_lock_t tag);对象创建不执行lock,对象销毁时执行 m.unlock()。所以m应该是一个已经被当前线程lock的互斥锁。

    template<class Mutex> class unique_lock;

    unique_lock()noexcept;不管理任何锁。

    explicit unique_lock(mutex_type &m);对象创建时执行 m.lock()。

    unique_lock(mutex_type &m,try_to_lock_t tag);对象创建时执行 m.try_lock()。

    unique_lock(mutex_type &m,defer_lock_t tag);对象创建时不进行上锁操作,m要满足没有被当前线程锁住的条件。

    unique_lock(mutex_type &m,adopt_lock_t tag);对象创建时不进行上锁操作,m要满足已经被当前线程锁住的条件。

    unique_lock(mutex_type &m,const duration & real_time);对象创建时执行 m.try_lock_for(real_time)。

    unique_lock(mutex_type &m,const time_point & abs_time);对象创建时执行 m.try_lock_until(abs_time)。 

    unique_lock(unique_lock &&);移动构造

    操作:unique_lock具备它所管理的锁的所有操作 lock、unlock、try_lock、try_lock_for、try_lock_until。

    mutex_type *release(); 不再管理互斥锁。

    void swap(unique_lock &);交换管理的互斥锁。

    bool owns_lock() 用于探测unique_lock是否管理着一个互斥锁且其处于上锁状态。bool operate bool() 与owns_lock等同。

    mutex_type * mutex();用于返回管理的互斥锁的指针,但仍对其进行管理。

    在unique_lock销毁的时候,owns_lock为真才会执行unlock。

    总的来说,lock_guard在时空间效率上比较高,但功能单一。unique_lock功能多,使用灵活,但时空间效率不如lock_guard。如果使用了辅助类来管理互斥锁,就不要直接操作锁了,否则容易引发混乱,产生BUG。

    辅助函数

    template <class Mutex1, class Mutex2, class... Mutexes>
    int try_lock (Mutex1& a, Mutex2& b, Mutexes&... cde);

    根据参数顺序对多个锁进行上锁,如果成功锁住所有锁,返回-1,返回值大于0表示失败的锁的位置号。

    template <class Mutex1, class Mutex2, class... Mutexes>
    void lock (Mutex1& a, Mutex2& b, Mutexes&... cde);

    对多个锁进行上锁,该函数是阻塞的。另,它保证发生异常的情况下已经上锁的锁会被解锁。

  • 相关阅读:
    WPF Caliburn 学习笔记(五)HelloCaliburn
    MSDN 教程短片 WPF 20(绑定3ObjectDataProvider)
    MSDN 教程短片 WPF 23(3D动画)
    比赛总结一
    HDU3686 Traffic Real Time Query System
    HDU3954 Level up
    EOJ382 Match Maker
    UESTC1565 Smart Typist
    HDU3578 Greedy Tino
    ZOJ1975 The Sierpinski Fractal
  • 原文地址:https://www.cnblogs.com/diegodu/p/9155613.html
Copyright © 2011-2022 走看看