C++11的互斥量

        互斥量是一种同步原语,是一种线程同步的手段,用来保护多线程同时访问的共享数据。

        C++11中提供了如下4种语义的互斥量(mutex):

        1、std::mutex:独占的互斥量,不能递归使用。

        2、std::mutex_mutex:带超时的独占互斥量,不能递归使用。

        3、std::recursive_mutex:递归互斥量,不带超时功能。

        4、std::recursive_timed_mutex:带超时的递归互斥量

独占互斥量std::mutex

        这些互斥量的基本接口很相似,一般用法是通过lock()方法来阻塞线程,直到获得互斥量的所有权为止。在线程获得互斥量并完成任务之后,就必须使用unlock()来解除对互斥量的占用,lock()和unlock()必须成对出现。try_lock()尝试锁定互斥量,如果成功则返回true,如果失败则返回false,它是阻塞的。std::mutex的基本用法如下代码。

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
using namespace std;std::mutex g_lock;void func()
{g_lock.lock();cout << "enter thread: " << std::this_thread::get_id() << endl;std::this_thread::sleep_for(std::chrono::seconds(1));cout << "leaving thread: " << std::this_thread::get_id() << endl;g_lock.unlock();
}///g++ mutex.cpp -lpthread
int main()
{std::thread t1(func);std::thread t2(func);std::thread t3(func);t1.join();t2.join();t3.join();return 0;
}

        输出结果如下:

enter thread: 140569127851776
leaving thread: 140569127851776
enter thread: 140568859412224
leaving thread: 140568859412224
enter thread: 140568590972672
leaving thread: 140568590972672

        使用lock_guard可以简化lock/unlock的写法,同时也更安全,因为lock_guard在构造函数时会自动锁定互斥量,而在退出作用域后进行析构时就会自动解锁,从而保证了互斥量的正确操作,避免忘记unlock操作,因此,应尽量用lock_guard。lock_guard用到了RAII技术,这种技术在类的构造函数中分配资源,在析构函数中释放资源,保证资源在出了作用域之后就释放,上面的例子使用lock_guard后更简洁,代码如下:

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
using namespace std;std::mutex g_lock;void func()
{std::lock_guard<std::mutex> locker(g_lock);///出了作用域之后自动解锁cout << "enter thread: " << std::this_thread::get_id() << endl;std::this_thread::sleep_for(std::chrono::seconds(1));cout << "leaving thread: " << std::this_thread::get_id() << endl;}///g++ mutex.cpp -lpthread
int main()
{std::thread t1(func);std::thread t2(func);std::thread t3(func);t1.join();t2.join();t3.join();return 0;
}

递归的独占互斥量std::recursive_mutex

        递归锁允许同一个线程多次获得该互斥锁,可以用来解决同一个线程需要多次获取互斥量死锁的问题。在下面的代码中,一个线程多次获取同一个互斥量时会发生死锁。

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
using namespace std;struct Complex
{
public:Complex(){i = 20;}void mul(int x){printf("%s %s %d\n", __FILE__, __func__, __LINE__);g_mutex.lock();///std::lock_guard<std::mutex> locker(g_mutex);i *= x;printf("%s %s %d\n", __FILE__, __func__, __LINE__);g_mutex.unlock();}void div(int x){printf("%s %s %d\n", __FILE__, __func__, __LINE__);g_mutex.lock();///std::lock_guard<std::mutex> locker(g_mutex);i /= x;printf("%s %s %d\n", __FILE__, __func__, __LINE__);g_mutex.unlock();}void both(int x, int y){///std::lock_guard<std::mutex> locker(g_mutex);g_mutex.lock();printf("%s %s %d\n", __FILE__, __func__, __LINE__);mul(x);div(y);g_mutex.unlock();printf("%s %s %d\n", __FILE__, __func__, __LINE__);}private:int i;std::mutex g_mutex;
};///g++ mutex.cpp -lpthread
int main()
{Complex complex;complex.both(2, 4);return 0;
}

        这个例子运行起来就会发生死锁,因为在调用both时获取了互斥量,之后再调用mul又要获取相同的互斥量,但是这个互斥量已经被当前线程获取了,无法释放,这时就会发生死锁。要解决这个死锁的问题,一个简单的办法就是用递归锁:std::recursive_mutex,它允许同一个线程多次获得互斥量。

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
using namespace std;struct Complex
{
public:Complex(){i = 20;}void mul(int x){printf("%s %s %d\n", __FILE__, __func__, __LINE__);g_mutex.lock();///std::lock_guard<std::recursive_mutex> locker(g_mutex);i *= x;printf("%s %s %d\n", __FILE__, __func__, __LINE__);g_mutex.unlock();}void div(int x){printf("%s %s %d\n", __FILE__, __func__, __LINE__);g_mutex.lock();///std::lock_guard<std::recursive_mutex> locker(g_mutex);i /= x;printf("%s %s %d\n", __FILE__, __func__, __LINE__);g_mutex.unlock();}void both(int x, int y){///std::lock_guard<std::recursive_mutex> locker(g_mutex);g_mutex.lock();printf("%s %s %d\n", __FILE__, __func__, __LINE__);mul(x);div(y);g_mutex.unlock();printf("%s %s %d\n", __FILE__, __func__, __LINE__);}private:int i;std::recursive_mutex g_mutex;
};void func()
{Complex complex;complex.both(2, 4);
}///g++ mutex.cpp -lpthread
int main()
{thread t1(func);t1.join();return 0;
}

        需要注意的是尽量不要使用递归锁,主要原因如下:

        1、需要用到递归锁定的多线程互斥处理往往本身就是可以简化的,允许递归互斥很容易放纵复杂逻辑的产生,从而导致一些多线程同步引起的问题。

        2、递归锁比起非递归锁,效率会低一些。

带超时的互斥量std::timed_mutex

        std::timed_mutex是超时的独占锁,主要用在获取锁时增加超时等待功能,因为有时不知道获取锁需要多久,为了不至于一直在等待获取互斥量,就设置一个等待超时时间,在超时后还可以做其他事情。

        std::timed_mutex比std::mutex多了两个超时获取锁的接口:try_lock_for和try_lock_until,这两个接口是用来设置获取互斥量的超时时间,使用时可以用while循环取不断地获取互斥量。std::timed_mutex的基本用法如下所示。

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
using namespace std;std::timed_mutex g_mutex;void work()
{std::chrono::milliseconds timeout(1000);while(true){if (g_mutex.try_lock_for(timeout)){cout << std::this_thread::get_id() << ": do work with the mutex" << endl;std::chrono::milliseconds sleepDuration(5000);std::this_thread::sleep_for(sleepDuration);g_mutex.unlock();std::this_thread::sleep_for(sleepDuration);}else{cout << std::this_thread::get_id() << ": do work without the mutex" << endl;std::chrono::milliseconds sleepDuration(2000);std::this_thread::sleep_for(sleepDuration);}}
}///g++ mutex.cpp -lpthread
int main()
{std::thread t1(work);std::thread t2(work);t1.join();t2.join();return 0;
}

        在上面的例子中,通过一个while循环不断地去获取超时锁,如果超时还没有获取到锁就会休眠,再继续获取超时锁。                

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

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

相关文章

zabbix分布式监控平台从IPV4切换到IPV6之监控主机切换

现在有一套监控了海量服务器的zabbix分布式监控平台需整体在线从IPV4切换到IPV6&#xff0c;不能影响其原有的定制监控及视图。本文讲解了切换的第一步--监控主机切换。 一、zabbix分布式监控平台平台架构 本套zabbix分布式监控平台是一个多代理服务器分布式部署的典型传统架构…

Node——Node.js简介

Node.js是一个基于Chrome V8引擎的JavaScript运行时环境&#xff0c;它能够让JavaScript脚本运行在服务端&#xff0c;这使得JavaScript成为与PHP、Python等服务端语言平起平坐的脚本语言。 1、认识Node.js Node.js是当今网站开发中非常流行的一种技术&#xff0c;它以简单易…

开放远程访问MySQL的权限

访问远程数据库时&#xff0c;产生Access denied for user ‘root‘‘xxx.xxx.xxx.xxx‘ (using password: YES)异常的解决办法 一. 异常现象 我编写了一个SpringBoot项目&#xff0c;项目中连接的数据库服务器地址是192.168.87.107&#xff0c;然后打包生成了对应的jar包&am…

Microsoft Remote Desktop高效、安全、稳定的远程办公解决方案

在今天的数字化时代&#xff0c;Remote Desktop远程办公已成为许多人的日常生活。无论你是因为工作需要&#xff0c;还是因为在家中需要访问公司服务器&#xff0c;微软远程连接软件都是一个理想的选择。 微软远程连接软件Remote Desktop是一款高效、安全、稳定的远程办公解决…

苹果手机照片恢复,这3个方法收藏好了吗?

如今&#xff0c;我们越来越喜欢用手机拍照来记录生活的点点滴滴。对于很多人来说&#xff0c;手机中的照片是他们珍贵的记忆和情感。如果这些照片丢失了&#xff0c;会给他们带来很大的困扰。那么&#xff0c;如何恢复苹果手机照片呢&#xff1f;本文将为您介绍有关苹果手机照…

内网穿透的应用-如何部署Tale博客并结合cpolar内网穿透发布个人站点到公网访问

Linux系统部署Tale个人博客并发布到公网访问 文章目录 Linux系统部署Tale个人博客并发布到公网访问前言1. Tale网站搭建1.1 检查本地环境1.2 部署Tale个人博客系统1.3 启动Tale服务1.4 访问博客地址 2. Linux安装Cpolar内网穿透3. 创建Tale博客公网地址4. 使用公网地址访问Tale…

阿里云效一键部署前后端

静态站点到OSS 阿里云-云效&#xff0c;阿里云企业级一站式 DevOps&#xff0c;可以免费使用&#xff08;会限制人数、流水线数量等&#xff0c;个人项目够用了&#xff09;。相关文章 CI 持续集成 - 阿里云云效 OSS 是对象存储的意思&#xff0c;一般一个项目对应一个 Bucke…

深度学习手势检测与识别算法 - opencv python 计算机竞赛

文章目录 0 前言1 实现效果2 技术原理2.1 手部检测2.1.1 基于肤色空间的手势检测方法2.1.2 基于运动的手势检测方法2.1.3 基于边缘的手势检测方法2.1.4 基于模板的手势检测方法2.1.5 基于机器学习的手势检测方法 3 手部识别3.1 SSD网络3.2 数据集3.3 最终改进的网络结构 4 最后…

STM32 外部中断配置与中断函数设计

单片机学习 目录 文章目录 一、外部中断配置步骤 1.1配置RCC 1.2配置GPIO 1.3配置AFIO 1.4配置EXTI 1.5配置NVIC 二、中断函数设计 总结 一、外部中断配置步骤 第一步&#xff1a;配置RCC&#xff0c;把涉及外设的时钟打开。第二步&#xff1a;配置GPIO&#xff0c;选择…

样品实验K-KAT348羧酸铋催化剂TDS说明书

样品实验K-KAT348羧酸铋催化剂TDS说明书 50克 100克 200克

STM32_11(SPI)

一、SPI通信 SPI&#xff08;Serial Peripheral Interface&#xff09;是由Motorola公司开发的一种通用数据总线四根通信线&#xff1a;SCK&#xff08;Serial Clock&#xff09;、MOSI&#xff08;Master Output Slave Input&#xff09;、MISO&#xff08;Master Input Slav…

TCP数据流的概念

发送端TCP数据流 考虑到实际网络中的各种影响&#xff0c;那么其实发送端调用send函数发送两个字符串hello, message和welcome的实际TCP分组可能有很多种。 第一种情况&#xff1a;一个TCP分组发出去 hello, message和welcome在一个TCP分组中发送出去&#xff0c;比如&#…

还在愁没项目?来瞧瞧这些另类赚钱方式

客套话不多说&#xff0c;直接上案例&#xff0c;这些都是正儿八经的真实案例&#xff0c;相信大家通过这些人的案例自然能摸索整理出一套属于自己的项目&#xff01;摸索不出&#xff0c;也多多少少能受一些灵感上的启发。 小A是长期混B站的&#xff0c;大家称他为“B站搬运工…

C++ 通过SQLite实现命令行工具

本文介绍了一个基于 C、SQLite 和 Boost 库的简单交互式数据库操作 Shell。该 Shell 允许用户通过命令行输入执行各种数据库操作&#xff0c;包括添加、删除主机信息&#xff0c;设置主机到特定主机组&#xff0c;以及显示主机和主机组列表。通过调用 SQLite3 库实现数据库连接…

【网络安全】meterpreter攻击实战

1.meterpreter 攻击成功后可以做什么指令&#xff1f; 远程控制命令执行摄像头监控密码获取创建后门用户破坏篡改系统。 2.创建后门用户并开启远程连接&#xff1a; net user zhangsan 123456/add && net localgroup adminstrators zhangsan/add exit run getgul -…

苍穹外卖--用户下单

代码开发 DTO设计 根据用户下单接口的参数设计DTO&#xff1a; 在sky-pojo模块&#xff0c;OrdersSubmitDTO.java已定义 package com.sky.dto;import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import java.io.Serializable; import java.math.Bi…

拓数派荣获上海市“智慧工匠”工业软件创新案例奖

近日&#xff0c;由上海市经济和信息化委员会指导、上海市城市数字化转型应用促进中心主办、上海中创产业创新研究院承办的“工业软件赋能新型工业化”主题沙龙暨2023“智慧工匠”工业软件创新案例竞赛颁奖典礼在上海圆满落幕。拓数派凭借上汽集团工业数据管理服务平台案例成功…

ROS vscode使用基本配置

1、创建ros工作空间 2、启动 vscode 3、vscode 中编译 ros ctrl shift B 调用编译&#xff0c;选择:catkin_make:build 修改.vscode/tasks.json 文件 4、 创建 ROS 功能包 选定 src ---> create catkin package 依次设置包名、添加依赖 5、C 实现 在功能包的 src 下…

Yolov8训练数据集过程 + 测试测试集 + 继续训练+报错解决

做自己第一次使用Yolov8训练的记录 1、下载代码 官网的我没找到对应的视频教程&#xff0c;操作起来麻烦&#xff0c;一下这个链接的代码可以有对应bilibili教程&#xff1a;完整且详细的Yolov8复现训练自己的数据集 选择这个下载&#xff1a; 2、安装需要的包&#xff1a; …

Qt之QOpenGLWidget开始3D显示

遇到第一个问题就是cmakelists的问题&#xff0c;提示“undefined reference to symbol ‘glXXXXX". 在target link中加入GL解决 SLAMBOOK2踩坑记之&#xff1a;plotTrajectory.cpp中的undefined reference to symbol ‘glTexImage2D‘错误_undefined reference to symbo…