漫话 C++11 之 mutex

在多线程的程序中,经常需要通过锁的机制来保证数据的一致性。

C++11提供了下面四种语义的锁:

type function desc
mutex Lockable 普通的互斥锁,不能递归使用。
timed_mutex TimedLockable 带超时的互斥锁,不能递归使用。
recursive_mutex Lockable 递归互斥锁。
recursive_timed_mutex TimedLockable 带超时的递归互斥锁。

继承关系为:

1
2
3
BasicLockable <--- Lockable <--- TimedLockable
+lock +try_lock +try_lock_for
+unlock +try_lock_until

mutex

C++11 中最基本的互斥量,它不支持递归地上锁。

  • mutex 不允许拷贝构造,也不允许 move 赋值,最初产生的 mutex 对象是处于 unlocked 状态的。
  • lock(),调用线程将锁住该互斥量。线程调用该函数会发生下面 3 种情况:
    1. 如果该互斥量当前没有被锁住,则调用线程将该互斥量锁住,直到调用 unlock之前,该线程一直拥有该锁。
    2. 如果当前互斥量被其他线程锁住,则当前的调用线程被阻塞住。
    3. 如果当前互斥量被当前调用线程锁住,则会产生死锁。
  • unlock(), 解锁,释放对互斥量的所有权。
  • try_lock(),尝试锁住互斥量,和 lock() 相似,不同的是如果互斥量被其他线程占有,则当前线程也不会被阻塞,而是马上返回 false

recursive_mutex

  • 允许同一个线程对互斥量多次上锁,来获得对互斥量对象的多层所有权。
  • 释放互斥量时需要调用与该锁层次深度相同次数的 unlock()

time_mutex

  • mutex 多了两个成员函数:try_lock_for()try_lock_until()
  • try_lock_for() 函数接受一个时间范围,表示在 一段时间范围之内 线程如果没有获得锁则被阻塞住,如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁,如果超时(即在指定时间内还是没有获得锁),则返回 false
  • try_lock_until() 函数则接受一个时间点作为参数,在 指定时间点未到来之前 线程如果没有获得锁则被阻塞住,如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁,如果超时(即在指定时间内还是没有获得锁),则返回 false

RAII

关于 mutex 的使用,建议使用 RAII(Resource Acquisition is Initialization)的方式,即在构造的时候 lock, 析构的时候 unlock, 不建议直接手工地进行 lock/unlock

C++11提供了 lock_guardunique_lock 两种简单而又安全的上锁和解锁方式,即使程序抛出了异常,先前已被上锁的 Mutex 对象也能正确进行解锁,极大地简化了与 Mutex 相关的异常处理代码。

1
2
3
4
5
template <class mutex> 
class lock_guard;

template <class mutex>
class unique_lock;
  • 都是模版类,实例化参数 mutex 必须是个 BasicLockable 类型(即支持 lockunlock)。
  • 都只负责在构造时对 mutex 加锁,析构时对 mutex 解锁,它们并不管理 mutex 本身的生命周期,因此,mutex 的生命周期应至少延伸至 lock_guardunique_lock 析构之后。

lock_guard

  • 只有构造函数和析构函数。

unique_lock

  • 更灵活的初始化方式:
    1. default
    2. locking
    3. try_locking
    4. deferred
    5. adopting
    6. locking_for
    7. locking_until
    8. move
  • 除了基本的上锁/解锁操作,unique_lock 还支持修改、获取 mutex 的操作:
    1. move 移动赋值
    2. swap 与另一个 unique_lock 对象交换它们所管理的 Mutex 对象的所有权
    3. release 返回指向它所管理的 Mutex 对象的指针,并释放所有权
    4. owns_lock 返回当前 unique_lock 对象是否获得了锁
    5. operator bool()owns_lock 功能相同 5. mutex 返回当前 unique_lock 对象所管理的 Mutex 对象的指针

参考

彦祖老师 wechat