C++11/14/17中提供的mutex系列类型如下:

互斥量

C++版本

作用

mutex

C++11

基本的互斥量

timed_mutex

C++11

timed_mutex带超时功能。在规定的等待时间内,没有获取锁,线程不会一直阻塞,代码会继续执行

recursive_mutex

C++11

递归锁,允许在同一个线程中同一个互斥量多次被 lock() ,用于可能被连续多次上锁(期间未解锁)的情况,效率要比mutex低

std::mutex 及其变种不允许同一个线程对互斥量多次上锁,而 std::recursive_mutex 则允许

recursive_timed_mutex

C++11

带超时的,递归的,独占互斥量,允许同一个线程,同一个互斥量,多次被lock,用法和非递归的一样

shared_timed_mutex

C++14

具有超时机制的可共享互斥量

shared_mutex

C++17


shared_mutex的弟弟曾实现是操作系统提供的读写锁,在多线程对共享资源读且少许县城对共享资源写的情况下,shared_mutex比mutex效率更高

写锁(排它锁):lock/unlock

读锁(共享锁):lock_shared/unlock_shared

以上系列的对象都提供了加锁(lock)、尝试加锁(try_lock)和解锁(unlock)方法。

为了避免死锁,std::mutex.lock方法和std:mutex.unlock方法需要成对使用,如果一个函数中有很多出口,而互斥体对象又是需要在整个面数作用域被保护的资源,那么我们在编码时会因为忘记在某个出口处调用std::mutex.unlock 而造成死锁。这时可以通过RAII技术封装这两个接口,C++新标准也提为我们提供了类似的封装:

互斥量管理

C++版本

作用

lock_guard

C++11

基于作用于的互斥量管理,在需要对资源进行保护的小范围作用域内,应首先考虑使用std::lock_guard

unique_lock

C++11

unique_lock 是 lock_guard 的升级加强版,一个通用的互斥量锁定包装器,它允许延迟锁定,限时深度锁定,递归锁定,锁定所有权的转移以及与条件变量一起使用。

shared_lock

C++14

shared_lock可用于保护共享数据不被多个线程同时访问

unique_lock专门管理“独占模式”,而shared_lock专门管理“共享模式”

scoped_lock

C++17

如果有多个mutex要同时lock,用scoped_lock。如果只要lock一个mutex,可以用lock_guard

如std::lock_guard:

void func()
{
std::lock quard<std:nutex> quard(mymutex);
//在这里放被保护的资源操作
}

mymutex 的类型是std:mutex,guard对象的构造函数会自动调用mymutex.lock 方法对 mymutex 进行加锁,在 guard 对象出了其作用域时,guard对象的析构函数会自动调用 mymutex.unlock 方法对 mymutex 进行解锁,简单来说:根据 RAII原则,在构造函数中上锁(创建即上锁),在析构函数中解锁(销毁即解锁)需要注意的是:mymutex 生命周期必须长于func 函数的作用域。

多线程使用锁经验总结:

  1. 减少锁的使用次数,能不用锁尽量不用;
  2. 明确锁的范围;
  3. 减少锁的使用粒度,尽量减少锁的作用的临界区代码范围。