c++ unique_lock与lock_guard的区别

来源:互联网 发布:杨氏模量测量实验数据 编辑:程序博客网 时间:2024/06/05 06:19

std::lock_guard是c++的模板类,定义如下:
template class lock_guard
lock_guard 对象通常用于管理某个锁(Lock)对象,因此与 Mutex RAII 相关,方便线程对互斥量上锁,即在某个 lock_guard 对象的声明周期内,它所管理的锁对象会一直保持上锁状态;而 lock_guard 的生命周期结束之后,它所管理的锁对象会被解锁。
模板参数Mutexx代表互斥量类型,他是一个基本的BasicLockable 类型,标准库中中定义的几种基本BasicLockable 类型,分别 std::mutex, std::recursive_mutex,std::timed_mutex,std::recursive_timed_mutex
以及std::unique_lock。
在lock_guard对象构造时,传入的Mutex对象会被当前线程锁住,在lock_guard析构时被解锁,所以不需要我们进行手动的解锁操作,提高了程序的正确率,尤其是当程序出现异常导致程序提前结束,有可能你的mutex对象还未被解锁就已经被跳过了,而lock_guard则替我们很好的解决了这个问题,无论程序是异常还是正常结束,lock_guard的析构函数总会被调用,即mutex对象总会被解锁,极大地简化了程序员编写与 Mutex 相关的异常处理代码。
观察下面的简单代码:

#include <iostream>       // std::cout#include <thread>         // std::thread#include <mutex>          std::mutex mtx;           // mutex for critical sectionvoid print_thread_id (int id) {  mtx.lock();  std::lock_guard<std::mutex> lck(mtx, std::adopt_lock);  std::cout << "thread #" << id << '\n';}int main (){  std::thread threads[10];  // spawn 10 threads:  for (int i=0; i<10; ++i)    threads[i] = std::thread(print_thread_id,i+1);  for (auto& th : threads) th.join();  return 0;}

在函数print_thread_id中,首先对mtx进行上锁操作,然后用mtx对象构造一个lock_guard对象,此时当前线程已经获得了锁,然后mtx的解锁操作由lock_guard的析构函数完成,即当lock_guard对象被销毁时,当前线程被自动解锁。
std::lock_guard最大的优点也是最大的缺点便是简单,没有给程序员提供足够的灵活度, 与我们便需要使用另一个与Mutex RAII的相关类unique_lock,该类与lock_guard相似。但unique_lock对象以独占所有权的方式管理mutex对象的上锁和解锁操作,所谓独占所有权,就是没有其他的unique_lock对象同时拥有某个mutex对象的所有权。
在构造(或移动(move)赋值)时,unique_lock 对象需要传递一个 Mutex 对象作为它的参数,新创建的 unique_lock 对象负责传入的 Mutex 对象的上锁和解锁操作。
unique_lock的构造函数:

  1. unique_lock() noexcept;
  2. explicit unique_lock(mutex_type& m);
  3. unique_lock(mutex_type& m, try_to_lock_t tag);
  4. unique_lock(mutex_type& m, defer_lock_t tag) noexcept;
  5. unique_lock(mutex_type& m, adopt_lock_t tag);
  6. template (class Rep, class Period)unique_lock(mutex_type& m, const chrono::durationRep,Period>& rel_time);
  7. template class Clock, class Duration
    unique_lock(mutex_type& m, const chrono::time_point Clock,Duration>& abs_time);
    (1) 默认构造函数
    新创建的 unique_lock 对象不管理任何 Mutex 对象。
    (2) locking 初始化
    新创建的 unique_lock 对象管理 Mutex 对象 m,并尝试调用 m.lock() 对 Mutex 对象进行上锁,如果此时另外某个 unique_lock 对象已经管理了该 Mutex 对象 m,则当前线程将会被阻塞。
    (3) try-locking 初始化
    新创建的 unique_lock 对象管理 Mutex 对象 m,并尝试调用 m.try_lock() 对 Mutex 对象进行上锁,但如果上锁不成功,并不会阻塞当前线程。
    (4) deferred 初始化
    新创建的 unique_lock 对象管理 Mutex 对象 m,但是在初始化的时候并不锁住 Mutex 对象。 m 应该是一个没有当前线程锁住的 Mutex 对象。
    (5) adopting 初始化
    新创建的 unique_lock 对象管理 Mutex 对象 m, m 应该是一个已经被当前线程锁住的 Mutex 对象。(并且当前新创建的 unique_lock 对象拥有对锁(Lock)的所有权)。
    (6) locking 一段时间(duration)
    新创建的 unique_lock 对象管理 Mutex 对象 m,并试图通过调用 m.try_lock_for(rel_time) 来锁住 Mutex 对象一段时间(rel_time)。
    (7) locking 直到某个时间点(time point)
    新创建的 unique_lock 对象管理 Mutex 对象m,并试图通过调用 m.try_lock_until(abs_time) 来在某个时间点(abs_time)之前锁住 Mutex 对象。
    unique_lock支持移动赋值操作,但普通的赋值被禁止了,即有move操作,但是禁止(operator = )操作。
    unique_lock的主要成员函数,
    1.上锁/解锁操作:lock,try_lock,try_lock_for,try_lock_until 和 unlock
    2.修改操作:移动赋值(move assignment)(前面已经介绍过了),交换(swap)(与另一个 std::unique_lock 对象交换它们所管理的 Mutex 对象的所有权),释放(release)(返回指向它所管理的 Mutex 对象的指针,并释放所有权)。
    3.获取属性操作:owns_lock(返回当前 std::unique_lock 对象是否获得了锁)、operator bool()(与 owns_lock 功能相同,返回当前 std::unique_lock 对象是否获得了锁)、mutex(返回当前 std::unique_lock 对象所管理的 Mutex 对象的指针)。
    返回std::unique_lock对象多管理的Mutex对象的指针
#include <iostream>       // std::cout#include <thread>         // std::thread#include <mutex>          // std::mutex, std::unique_lock, std::defer_lockclass MyMutex : public std::mutex {  int _id;public:  MyMutex (int id) : _id(id) {}  int id() {return _id;}};MyMutex mtx (101);void print_ids (int id) {  std::unique_lock<MyMutex> lck (mtx);  std::cout << "thread #" << id << " locked mutex " << lck.mutex()->id() << '\n';}int main (){  std::thread threads[10];  // spawn 10 threads:  for (int i=0; i<10; ++i)    threads[i] = std::thread(print_ids,i+1);  for (auto& th : threads) th.join();  return 0;}
原创粉丝点击