zoukankan      html  css  js  c++  java
  • 关于lock_guard和unique_lock构造函数第二参数的作用介绍

    一、lock_guard和unique_lock锁

      在并发编程中,我们常常使用标准库中的mutex互斥对象实现线程同步;但是如果在开发中裸用mutex对象,当某个线程锁住临界资源或临界区且在未解锁的情况下异常退出,那么其他线程将始终无法访问临界资源或临界区。lock_guard和unique_lock类恰恰能够帮我们自动管理可锁对象,即使异常情况下也能自动对管理的可锁对象进行解锁。例如

    1 {
    2     std::lock_guard<std::mutex> lock(_mtx);   // 自动上锁和解锁
    3     .....
    4 }
    5 
    6 {
    7   std::unique_lock<std::mutex> lock(_mtx);  // 自动上锁和解锁
    8   ......  
    9 }

      unique_lock相交于lock_guard更加灵活,可以手动进行解锁,但是在日常编程中,还是以lock_guard为主。但是标准库也提供了第二参数的构造函数。例如:

    1 explicit lock_guard (mutex_type& m);
    2 lock_guard (mutex_type& m, adopt_lock_t tag);
    3 
    4 
    5 explicit unique_lock (mutex_type& m);
    6 unique_lock (mutex_type& m, try_to_lock_t tag);    
    7 unique_lock (mutex_type& m, defer_lock_t tag) noexcept;    
    8 unique_lock (mutex_type& m, adopt_lock_t tag);
    9 ... ...

      接下来就简单介绍下第二参数的作用吧。

    二、lock_guard和unique_lock第二参数的作用

    1 std::lock_guard<std::mutex> lock(g_mtx, std::adopt_lock);

      对于lock_guard第二参数类型只有一种,锁管理器构造的时候不会自动对可锁对象上锁;由可锁对象自己加锁;等锁管理器析构的时候自动解锁。

    1  {
    2     std::lock_guard<std::mutex> lock(g_mtx, std::adopt_lock);
    3     g_mtx.lock();
    4     临界区或临界资源
    5 } 

      或者

    1 {
    2     g_mtx.lock();
    3     std::lock_guard<std::mutex> lock(g_mtx, std::adopt_lock);
    4     临界区或临界资源
    5 } 

      如果我们指定了第二参数,但是没有lock,锁管理器析构的时候解锁了无拥有权的可锁对象,导致异常。

       有同学会问,为啥要使用第二参数构造函数;直接在锁管理器构造的时候自动加锁不好吗?其实官方提供第二参数构造函数是有其他作用的。比如多锁场景下,我们会调用std::lock避免死锁的出现,但是这个方法要求锁管理

    器不能拥有可锁对象,由std::lock方法执行锁操作。如果没有提供第二参数构造函数,那么我们就无法使用该方法了。

    1 {
    2      std::unique_lock<std::mutex> lock(g_mtx, std::adopt_lock);
    3       g_mtx.lock();
    4       临界区或临界资源
    5 }
    1 {
    2     g_mtx.lock();
    3     std::unique_lock<std::mutex> lock(g_mtx, std::adopt_lock);
    4     临界区或临界资源     
    5 }

      std::adopt_lock和lock_guard第二参数作用类似。锁管理器假设拥有可锁对象,在锁管理器析构的时候自动解锁。注意:使用该参数类型构造的锁管理器必须只能通过可锁对象进行lock,不可通过锁管理器进行lock,误用会导致程序异常,具体原因可以参考

    unique_lock源码。

    1 {
    2    std::unique_lock<std::mutex> lock(g_mtx, std::defer_lock);
    3     lock.lock();           // 不能用g_mtx.lock(),第二次锁的时候会崩溃
    4     临界区或临界资源
    5 }
      std::defer_lock参数作用:锁管理器在构造的时候不主动lock且不拥有可锁对象;如果后续执行lock,锁管理器析构的时候自动解锁。注意:该类型构造的锁管理器只能通过锁管理器执行lock且拥有可锁对象。如果直接调用可锁对象进行锁操作后,会导致程序
    异常;详情可参考源码。

    1 {
    2      std::unique_lock<std::mutex> lock(g_mtx, std::try_to_lock);
    3      if (lock.owns_lock()) {
    4          临界区或临界资源
    5      }
    6 }
      std::try_to_lock参数作用:锁管理器在构造的时候尝试lock;如果lock上,锁管理器就拥有可锁对象(持有锁),析构的时候自动执行解锁;否则就不持可锁对象,析构的时候也就不会解锁;它的好处是当某个线程尝试获取该该锁,但是该锁已经已被其他线程持有,
    那么不会出现线程被阻塞挂起。
     
  • 相关阅读:
    如何用redis/memcache做Mysql缓存层?
    孤儿进程和僵尸进程总结
    二叉树的遍历(非递归)
    Linux进程分配内存的两种方式--brk() 和mmap()
    Hbase
    cgroup 分析之CPU和内存部分
    十道海量数据处理面试题与十个方法大总结
    快速定位性能瓶颈,检查出所有资源(CPU、内存、磁盘IO等)的利用率(utilization)、饱和度(saturation)和错误(error)度量,即USE方法
    红黑树
    tcp 两个重要窗口:滑动窗口 和 拥塞窗口
  • 原文地址:https://www.cnblogs.com/smartNeo/p/14624156.html
Copyright © 2011-2022 走看看