c++11 unique_lock 使用

news2025/8/3 15:27:37

        我们知道c++11 提供了很多的类模板供我们使用,其中就有 lock_guard,这个用法也很简单,像这样:

std::mutex m_mutex;
std::lock_guard<std::mutex> guard(m_mutex);

mutex 的 lock 和 unlock 完全是自动的,无需我们手动操作。因为模板类 lock_guard 在构造函数里进行了 lock,在析构函数里进行了 unlock。我们可以看源码实现:

  template<typename _Mutex>
    class lock_guard
    {
    public:
      typedef _Mutex mutex_type;

      explicit lock_guard(mutex_type& __m) : _M_device(__m)
      { _M_device.lock(); }

      lock_guard(mutex_type& __m, adopt_lock_t) : _M_device(__m)
      { } // calling thread owns mutex

      ~lock_guard()
      { _M_device.unlock(); }

      lock_guard(const lock_guard&) = delete;
      lock_guard& operator=(const lock_guard&) = delete;

    private:
      mutex_type&  _M_device;
    };

和 lock_guard 具有同样功能的还有一个:unique_lock。同样的,unique_lock 也是可以自动锁定和解锁,但其多了几个接口,且构造函数里也多了一些参数。

1,和 lock_guard 一样的功能,如:

#include <thread>
#include <mutex>
#include <chrono>
#include <stdio.h>

std::mutex mtx;

void funcProc()
{
    std::unique_lock<std::mutex> guard(mtx);
    int count = 5;

    while(count-- > 0)
    {
        printf("in new thread\n");
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
}

int main()
{
    std::thread t1(funcProc);

    //先让子线程跑起来再去获取锁
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    printf("main thread begin to get mtx\n");
    {
        std::unique_lock<std::mutex> guard(mtx);
        int count = 5;
        while(count -- > 0)
        {
            printf("main thread already get mtx\n");
            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        }
    }

    t1.join();
    return 0;
}

很简单,子线程先获取锁,执行完后 main 线程获取锁,这里完全靠自动锁定和解锁。

2,构造函数里参数 std::defer_lock

当我们在构造函数里添加参数 std::defer_lock 时,此时构造函数里并没有调用锁的 lock(),即之后需要我们手动去调用 lock() 进行锁定,如:

#include <thread>
#include <mutex>
#include <chrono>
#include <stdio.h>

std::mutex mtx;

void funcProc()
{
    //加了defer_lock, 构造函数里不会进行lock
    std::unique_lock<std::mutex> guard(mtx, std::defer_lock);
    int count = 5;
    std::this_thread::sleep_for(std::chrono::milliseconds(10));

    //这里尝试锁定,但此锁已经被 main 线程锁定了
    guard.lock();
    while(count-- > 0)
    {
        printf("in new thread\n");
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
}

int main()
{
    std::thread t1(funcProc);

    printf("main thread begin to get mtx\n");
    {
        std::unique_lock<std::mutex> guard(mtx);
        int count = 5;
        while(count -- > 0)
        {
            printf("main thread already get mtx\n");
            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        }
        
    }
    
    //这里已经解锁,子线程可以获取锁了
    printf("main thread unlock\n");
    t1.join();
    return 0;
}

 

我们看源码里,带defer_lock参数的构造函数是这样的:

      unique_lock(mutex_type& __m, defer_lock_t) noexcept
      : _M_device(&__m), _M_owns(false)
      { }

 3,lock() 函数

相比 lock_guard ,unique_lock 可以进行手动 lock,但如果是已经lock的情况下再lock时,则会抛出异常,如:

#include <thread>
#include <mutex>
#include <chrono>
#include <stdio.h>

std::mutex mtx;

void funcProc()
{

    std::unique_lock<std::mutex> guard(mtx);
    int count = 5;

    //当前线程已经锁定时,再调用 lock() 则会抛出异常
    guard.lock();
    while(count-- > 0)
    {
        printf("in new thread\n");
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
}

int main()
{
    std::thread t1(funcProc);

    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    printf("main thread begin to get mtx\n");
    {
        std::unique_lock<std::mutex> guard(mtx);
        int count = 5;
        while(count -- > 0)
        {
            printf("main thread already get mtx\n");
            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        }
        
    }
    
    //这里已经解锁,子线程可以获取锁了
    printf("main thread unlock\n");
    t1.join();
    return 0;
}

4,unlock 函数

在没有 lock 的情况下进行 unlock 也会抛出异常,如:

#include <thread>
#include <mutex>
#include <chrono>
#include <stdio.h>

std::mutex mtx;

void funcProc()
{

    std::unique_lock<std::mutex> guard(mtx, std::defer_lock);
    int count = 5;

    //没有锁定的情况下解锁,则抛出异常
    guard.unlock();
    while(count-- > 0)
    {
        printf("in new thread\n");
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
}

int main()
{
    std::thread t1(funcProc);

    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    printf("main thread begin to get mtx\n");
    {
        std::unique_lock<std::mutex> guard(mtx);
        int count = 5;
        while(count -- > 0)
        {
            printf("main thread already get mtx\n");
            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        }
    }
    
    //这里已经解锁,子线程可以获取锁了
    printf("main thread unlock\n");
    t1.join();
    return 0;
}

5,release 函数

 注意这个 release 函数,它并不会解锁,所以这个函数慎用,否则就是死锁,如:

#include <thread>
#include <mutex>
#include <chrono>
#include <stdio.h>

std::mutex mtx;

void funcProc()
{
    std::unique_lock<std::mutex> guard(mtx);
    int count = 5;

    while(count-- > 0)
    {
        printf("in new thread\n");
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        if(count == 2)
        {
            printf("in new thread, release mtx\n");
            //release()并没有解锁,main 线程则会一直在等锁,若要解锁则:guard.release()->unlock()
            guard.release();
        }
    }
}

int main()
{
    std::thread t1(funcProc);

    //先让子线程跑起来再去获取锁
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    printf("main thread begin to get mtx\n");
    {
        std::unique_lock<std::mutex> guard(mtx);
        int count = 5;
        while(count -- > 0)
        {
            printf("main thread already get mtx\n");
            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        }
    }

    t1.join();
    return 0;
}

 我们看 release 的源码 :

      mutex_type*
      release() noexcept
      {
	mutex_type* __ret = _M_device;
	_M_device = 0;
	_M_owns = false;
	return __ret;
      }

所以我们要解锁的话,得这样调用:guard.release()->unlock()。

其他函数这里就不讲了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/34801.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

新鲜出炉|基于深度学习的运维日志领域新进展

作者&#xff1a;云智慧算法工程师 Hugo Guo 运维日志领域研究方向主要包含异常日志检测、日志模式解析、日志内容分类、日志告警等。本篇文章介绍了热门异常检测模型 DeepLog、A2Log 等模型&#xff0c;以及云智慧自研模型 Translog 等。与此同时&#xff0c;在文章最后介绍了…

springboot自定义starter实践

创建一个spring项目 仿照spring的规范&#xff0c;artifact命名为xxx-spring-boot-starter 按需添加必要的依赖 这里只作为测试&#xff0c;就按最低的需求来只勾选如下三个 lombok、spring-boot-configuration-processor、spring-boot-autoconfigure 默认生成的项目结构如下…

使用HuggingFace实现 DiffEdit论文的掩码引导语义图像编辑

在本文中&#xff0c;我们将实现Meta AI和Sorbonne Universite的研究人员最近发表的一篇名为DIFFEDIT的论文。对于那些熟悉稳定扩散过程或者想了解DiffEdit是如何工作的人来说&#xff0c;这篇文章将对你有所帮助。 什么是DiffEdit? 简单地说&#xff0c;可以将DiffEdit方法…

【论文解读】Self-Explaining Structures Improve NLP Models

&#x1f365;关键词&#xff1a;性能提升、文本分类、信息推理 &#x1f365;发表期刊&#xff1a;arXiv 2020 &#x1f365;原始论文&#xff1a;https://arxiv.org/pdf/2012.01786.pdf &#x1f365;代码链接https://github.com/ShannonAI/Self_Explaining_Structures_Impro…

Java内部类

Java当中内部类主要有4种&#xff0c;分别是静态内部类、实例内部类/非静态内部类、局部内部类&#xff08;几乎不用&#xff09;、匿名内部类。静态内部类&#xff1a;被static修饰的内部成员类 ①在静态内部类只能访问外部类中的静态成员 ②创建静态内部类对象时&#xff0c;…

PB 2019 R3 MSOLEDBSQL SQL Server not available in Database Profiles

pb2019 pb2021,安装SQL OLEDB驱动时的注意事项&#xff1a; I installed PB 2019 R3, but when I go to the Database Profiles Window, MSOLEDBSQL SQL Server is not listed. Please go to the registry to check and see the key MSOLEDBSQL exists under HKEY_LOCAL_MACHI…

Python之tkinter图形界面设计学习二

图形用户界面&#xff08;简称GUI&#xff09;&#xff0c;是指采用图形方式显示的计算机操作用户界面。与计算机的命令行界面相比&#xff0c;图形界面对于用户的操作显得更加直观和简便。 一、tkinter模块 tkinter是Python的内置GUI模块。使用tkinter可以快速地创建GUI应用…

Vue脚手架Ⅲ(浏览器本地存储,Vue中的自定义事件,全局事件总线,消息订阅与发布(pubsub),nextTick,Vue封装的过度与动画)

文章目录脚手架3.10 浏览器本地存储3.11 Vue中的自定义事件3.12 全局事件总线3.13 消息订阅与发布&#xff08;pubsub&#xff09;3.14 nextTick3.15 Vue封装的过度与动画3.15.1 动画效果3.15.2 过度效果3.15.3 多个元素过度3.15.4 集成第三方动画3.15.5 总结过度和动画脚手架 …

达梦数据库安装与初始化超详细教程

陈老老老板&#x1f9b8;&#x1f468;‍&#x1f4bb;本文专栏&#xff1a;国产数据库-达梦数据库&#xff08;主要讲一些达梦数据库相关的内容&#xff09;&#x1f468;‍&#x1f4bb;本文简述&#xff1a;本文讲一下达梦数据库的下载与安装教程&#xff08;Windows版&…

一文解读 NFT 零版税

当我们听到“版税”这两个字时&#xff0c;脑海中首先浮现的是什么&#xff1f; 是对创作者作品权属的保护&#xff0c;还是项目方、平台额外的收益&#xff1f; 长期以来&#xff0c;版税作为一种收益机制&#xff0c;让买家“为知识和内容付费”&#xff0c;又让卖家“享受…

m在ISE平台下使用verilog开发基于FPGA的GMSK调制器

目录 1.算法描述 2.仿真效果预览 3.MATLAB部分代码预览 4.完整MATLAB程序 1.算法描述 高斯最小频移键控&#xff08;Gaussian Filtered Minimum Shift Keying&#xff09;&#xff0c;这是GSM系统采用的调制方式。数字调制解调技术是数字蜂窝移动通信系统空中接口的重要组成…

决策树-相关作业

1. 请使用泰勒展开推导gini不纯度公式&#xff1b; 2. 请说明树的剪枝怎么实现&#xff1b; ●预剪枝&#xff08;pre-pruning&#xff09;通过替换决策树生成算法中的停止准则。&#xff08;例如&#xff0c;最大树深度或信息增益大于某一阈值&#xff09;来实现树的简化。预…

Flutter高仿微信-第39篇-单聊-删除单条信息

Flutter高仿微信系列共59篇&#xff0c;从Flutter客户端、Kotlin客户端、Web服务器、数据库表结构、Xmpp即时通讯服务器、视频通话服务器、腾讯云服务器全面讲解。 详情请查看 效果图&#xff1a; 实现代码&#xff1a; //删除对话框 Future<void> _showDeleteDialog(Ch…

要把项目问题管理好,项目经理需要这8个步骤!

项目问题时有发生&#xff0c;想让项目获得成功&#xff0c;项目经理需要有一个计划来快速有效地应对任何出现的问题。这是最佳实践问题管理过程的一部分&#xff0c;更是良好项目管理的核心本质。 项目问题的四种类型 任何事情都可能成为项目过程中影响项目计划的问题。项目…

ZYNQ之FPGA学习----FIFO IP核使用实验

1 FIFO IP核介绍 FIFO 的英文全称是 First In First Out&#xff0c; 即先进先出。与 FPGA 内部的 RAM 和 ROM 的区别是没有外部读写地址线&#xff0c; 采取顺序写入数据&#xff0c; 顺序读出数据的方式&#xff0c;使用起来简单方便&#xff0c;缺点就是不能像 RAM 和 ROM …

共创可持续出行未来 奔驰牵手《阿凡达:水之道》

11月20日&#xff0c;梅赛德斯-奔驰与20世纪影业及其出品电影《阿凡达&#xff1a;水之道》的品牌战略合作迈入崭新篇章&#xff01;电影《阿凡达&#xff1a;水之道》已定档于12月16日在全球多地公映&#xff0c;并于即日起开启主题为——“地球&#xff0c;我们的潘多拉”的联…

医疗设备远程监控 5G千兆工业网关智慧医疗

医疗设备远程监控 5G千兆工业网关智慧医疗 5G千兆工业网关的医疗设备远程监控应用&#xff0c;实现各医疗智能终端连接入网&#xff0c;医疗数据、监控视频、设备状态数据等&#xff0c;实时采集&#xff0c;边缘节点分析处理&#xff0c;低延时高速传输&#xff0c;工作人员远…

外汇天眼:外汇投资入门知识炒汇者的心理误区有哪些?

今天这篇文章我们了解一下关于外汇炒汇者的心理误区有哪些&#xff0c;希望对大家进行外汇投资有所帮助。 盲目跟风--心理误区之一 股市被动受诸多复杂因素的影响&#xff0c;其中股友的跟风心理对股市影响甚大。有这种心理的投资人&#xff0c;看见他人纷纷购进股票时&#…

代码随想录训练营第31天|LeetCode 455.分发饼干、 376. 摆动序列、53. 最大子序和

参考 代码随想录 什么是贪心算法 贪心算法&#xff08;又称贪婪算法&#xff09;是指&#xff0c;在对问题求解时&#xff0c;总是做出在当前看来是最好的选择。也就是说&#xff0c;不从整体最优上加以考虑&#xff0c;算法得到的是在某种意义上的局部最优解 。 贪心算法不…

hive表加载csv格式数据或者json格式数据

先说简单的使用 CREATE TABLE cc_test_serde( id string COMMENT from deserializer, name string COMMENT from deserializer) ROW FORMAT SERDE org.apache.hadoop.hive.serde2.JsonSerDe STORED AS INPUTFORMAT org.apache.hadoop.mapred.TextInputFormat OUTPUTFO…