C++多线程3-共享数据操作保护

2023-05-16

目录:
1.多线程操作共享数据引出的问题
2.解决多线程操作共享数据的方法:锁
3.互斥量mutex的概念和用法
4.lock普通锁的用法
5.lock_guard类模板的用法
6.死锁的概念和解决
7.unique_lock的用法

1.多线程操作共享数据引出的问题
代码举例:

//我们用Test::set函数中作为多线程的回调函数,在这个回调函数中打印vector的size,发现它会出现段错误。
#include <iostream>
#include <thread>
#include <memory>
#include <vector>
#include <string>

using namespace std;

class Test
{
public:
	void set(const int index)
	{
		string str = "hello" + to_string(index);
		str_mv.push_back(str);
		cout << "线程id=" << std::this_thread::get_id() << ",size=" << str_mv.size() << endl;
	}
	
private:
	vector<string> str_mv;
};

int main()
{
	Test test;
	thread th[10000];
	for (int i=0; i<10000; i++)
	{
		th[i] = thread(&Test::set, &test, i);
	}
	
	for (int i=0; i<10000; i++)
	{
		if (th[i].joinable())
			th[i].join();
	}
	return 0;
}

//打印结果
root@epc:/home/share/test#  ./test
...
线程id=139743976642304,size=4086
线程id=139743968249600,size=4087
线程id=139743959856896,size=4088
线程id=139743951464192,size=4089
Segmentation fault
root@epc:/home/share/test# 

2.解决多线程操作共享数据的方法:锁
在多线程操作共享数据时,会存在多个线程同时访问共享数据的情况,如果它们的操作只是读,那么没有问题,但是如果同时进行写和读就会出现问题,很容易造成内存访问错误。因此我们通过锁的机制,使得线程排队访问共享数据。

3.互斥量mutex的概念和用法
互斥量的作用是管理共享资源或一小段代码。它有两种状态解锁和加锁。当多个线程访问共享资源时,将随机选择一个线程并分配锁去处理共享资源,其余线程则会被阻塞在该互斥量上,待该线程完事后,互斥量将锁交给另外一个线程,依次循环,直至所有线程处理完毕。
互斥量的头文件是mutex,它对于所有线程都是唯一的因此必须声明它为全局变量或者类静态成员。

//互斥量的普通锁举例
#include <iostream>
#include <thread>
#include <memory>
#include <vector>
#include <string>
#include <mutex>

using namespace std;

class Test
{
public:
	void set(const int index)
	{
		mutex_m.lock();
		string str = "hello" + to_string(index);
		str_mv.push_back(str);
		cout << "线程id=" << std::this_thread::get_id() << ",size=" << str_mv.size() << endl;
		mutex_m.unlock();
	}
	
private:
	vector<string> str_mv;
	static mutex mutex_m;
};

mutex Test::mutex_m;

int main()
{
	Test test;
	thread th[10000];
	for (int i=0; i<10000; i++)
	{
		th[i] = thread(&Test::set, &test, i);
	}
	
	for (int i=0; i<10000; i++)
	{
		if (th[i].joinable())
			th[i].join();
	}
	return 0;
}
//打印结果
root@epc:/home/share/test#  ./test
...
线程id=140531608835840,size=9998
线程id=140531617228544,size=9999
线程id=140531625621248,size=10000
root@epc:/home/share/test# 

4.lock普通锁的用法
互斥量使用了lock()对资源上锁后,一定要调用unlock()进行解锁,而且顺序不可颠倒,否则执行程序会报异常。lock()和unlock()的位置限定了加锁资源的范围。但很多时候,程序员很容易会忘记unlock()的使用,因此C++引入了类似于智能指针机制的模板类,比如lock_guard,当它的对象生存期结束时,析构函数会调用unlock()函数。
异常结果如下:
root@epc:/home/share/test# ./test
线程id=139657018976000,size=1
terminate called after throwing an instance of ‘std::system_error’
what(): Resource temporarily unavailable
Aborted
root@epc:/home/share/test#

5.lock_guard类模板的用法
lock_guard使用比较简单高效,构造函数时拿到锁,析构函数时释放锁。

//lock_guard类模板的用法
class Test
{
public:
	void set(const int index)
	{
		lock_guard<mutex> lock(mutex_m);
		string str = "hello" + to_string(index);
		str_mv.push_back(str);
		cout << "线程id=" << std::this_thread::get_id() << ",size=" << str_mv.size() << endl;
	}
	
private:
	vector<string> str_mv;
	static mutex mutex_m;
};

mutex Test::mutex_m;

6.死锁的概念和解决
死锁最直观的现象是程序没有反应,此时陷入所有线程被阻塞的状态。根据如下例子分析,死锁的根本原因就是当存在多个锁时它们锁的顺序不一致,解决的办法就是锁的顺序保持一致。
解决办法:

//死锁的解决方法:锁的顺序保持一致
class Test
{
public:
	void set()
	{
		//线程执行这段代码需要先获取mutex1_m锁和mutex2_m锁;
		lock_guard<mutex> lock1(mutex1_m);	
		lock_guard<mutex> lock2(mutex2_m);
		string str = "hello";
		str_mv.push_back(str);
		cout << "线程id=" << std::this_thread::get_id() << "进行set" << endl;
	}
	
	void get()
	{
		//线程执行这段代码需要先获取mutex1_m锁和mutex2_m锁;
		lock_guard<mutex> lock1(mutex1_m);
		lock_guard<mutex> lock2(mutex2_m);
		cout << "线程id=" << std::this_thread::get_id() << "进行get" << endl;
	}
	
private:
	vector<string> str_mv;
	static mutex mutex1_m;
	static mutex mutex2_m;
};

死锁代码用如下代码举例:

//死锁代码用如下代码举例:
//线程A执行set函数需先获取mutex1_m锁然后获取mutex2_m锁,线程B执行get函数需先获取mutex2_m锁然后获取mutex1_m锁;
//存在这种情况,当线程A获取到mutex1_m锁时,发现无法获取mutex2_m锁,原因就是线程B正在使用mutex2_m锁,
//于是等待线程B释放mutex2_m锁,同时线程B也在等待线程A释放mutex1_m锁;这就造成了死锁的状态,大家都处于阻塞状态。
#include <iostream>
#include <thread>
#include <memory>
#include <vector>
#include <string>
#include <mutex>

using namespace std;

class Test
{
public:
	void set()
	{
		//线程执行这段代码需要先获取mutex1_m锁和mutex2_m锁;
		lock_guard<mutex> lock1(mutex1_m);	
		lock_guard<mutex> lock2(mutex2_m);
		string str = "hello";
		str_mv.push_back(str);
		cout << "线程id=" << std::this_thread::get_id() << "进行set" << endl;
	}
	
	void get()
	{
		//线程执行这段代码需要先获取mutex2_m锁和mutex1_m锁;
		lock_guard<mutex> lock2(mutex2_m);
		lock_guard<mutex> lock1(mutex1_m);
		cout << "线程id=" << std::this_thread::get_id() << "进行get" << endl;
	}
	
private:
	vector<string> str_mv;
	static mutex mutex1_m;
	static mutex mutex2_m;
};

mutex Test::mutex1_m;
mutex Test::mutex2_m;

int main()
{
	Test test;
	thread th1[5000];
	thread th2[5000];
	for (int i=0; i<5000; i++)
	{
		th1[i] = thread(&Test::set, &test);
		th2[i] = thread(&Test::get, &test);
	}
	
	for (int i=0; i<5000; i++)
	{
		if (th1[i].joinable())
			th1[i].join();
		if (th2[i].joinable())
			th2[i].join();
	}
	return 0;
}

7.unique_lock的用法
内容比较多,会在后续补充。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++多线程3-共享数据操作保护 的相关文章

  • 百练_2680:化验诊断

    描述 下表是进行血常规检验的正常值参考范围 xff0c 及化验值异常的临床意义 xff1a 给定一张化验单 xff0c 判断其所有指标是否正常 xff0c 如果不正常 xff0c 统计有几项不正常 化验单上的值必须严格落在正常参考值范围内
  • encodeURIComponent()的使用

    在使用springMVC的过程中 若想让前台传递的字符串让后台用Date类型自动解析可以通过initBinder函数来实现 之前我用表单提交方式向后台传递时间格式的数据 2013 03 06 22 00 00 可以正确解析 但用自己手动拼接
  • 百练_2718:晶晶赴约会

    描述 晶晶的朋友贝贝约晶晶下周一起去看展览 xff0c 但晶晶每周的1 3 5有课必须上课 xff0c 请帮晶晶判断她能否接受贝贝的邀请 xff0c 如果能输出YES xff1b 如果不能则输出NO 输入 输入有一行 xff0c 贝贝邀请晶
  • 百练_2723:不吉利日期

    描述 在国外 xff0c 每月的13号和每周的星期5都是不吉利的 特别是当13号那天恰好是星期5时 xff0c 更不吉利 已知某年的一月一日是星期w xff0c 并且这一年一定不是闰年 xff0c 求出这一年所有13号那天是星期5的月份 x
  • 数据结构_练习 第4章 串、数组和广义表

    1 xff0e 选择题 xff08 1 xff09 串是一种特殊的线性表 xff0c 其特殊性体现在 xff08 xff09 A xff0e 可以顺序存储 B xff0e 数据元素是一个字符 C xff0e 可以链式存储 D xff0e 数
  • 数据机构_练习 第6章 图

    1 xff0e 选择题 xff08 1 xff09 在一个图中 xff0c 所有顶点的度数之和等于图的边数的 xff08 xff09 倍 A xff0e 1 2 B xff0e 1 C xff0e 2 D xff0e 4 答案 xff1a
  • 数据结构_练习 第7章  查找

    1 xff0e 选择题 xff08 1 xff09 对 n 个元素的表做顺序查找时 xff0c 若查找每个元素的概率相同 xff0c 则平均查找长度为 xff08 xff09 A xff0e n 1 2 B xff0e n 2 C xff0
  • 数据结构_练习 第8章 排序

    1 xff0e 选择题 xff08 1 xff09 从未排序序列中依次取出元素与已排序序列中的元素进行比较 xff0c 将其放入已排序序列的正确位置上的方法 xff0c 这种排序方法称为 xff08 xff09 A xff0e 归并排序 B
  • Java GUI小程序--画板

    画板 一个可以绘画的简单绘图软件 本文用两个类来实现画板的基本功能 xff08 源代码在文章最后面 xff09 画板制作分两个部分 xff1a xff08 一 xff09 界面布局 xff08 二 xff09 实现功能 小知识 xff1a
  • Java_关于垃圾回收机制(GC)的六个知识

    1 垃圾回收机制 xff1a 垃圾回收是一种动态存储管理技术 xff0c 它自动地释放不再被程序引用的对象 xff0c 按照特定的垃圾收集算法来实现资源自动回收的功能 当一个对象不再被引用的时候 xff0c 内存回收它占领的空间 xff0c
  • 树莓派 Nginx+php7搭建网站

    1 6 安装Nginx 43 php7 并且启动 sudo apt get update sudo apt get install nginx sudo apt get install php7 0 fpm php7 0 cli php7
  • Python的GUI编程(一)Label(标签)

    常用Python GUI库有 xff1a 1 Tkinter 2 WxPython 3 PyQT 4 pyGtk 5 Jython 6 MFC 7 PythonCard 8 Dabo 9 AnyGui 10 WPY 11 IronPytho
  • 使用 Drools 规则引擎实现业务逻辑

    要求施加在当今软件产品上的大多数复杂性是行为和功能方面的 xff0c 从而导致组件实现具有复杂的业务逻辑 实现 J2EE 或 J2SE 应用程序中业务逻辑最常见的方法是编写 Java 代码来实现需求文档的规则和逻辑 在大多数情况下 xff0
  • Python的GUI编程(九)Menu(菜单)OptionMenu(为可选菜单)

    在用户界面程序中 菜单以图标和文字的方式展示可用选项 用鼠标选择一个选项 程序的某个行为既被触发 这种行为通常包括比如 打开 保存文件 退出程序 等功能 上下文菜单是一种根据用户当前所在程序位置 上下文 动态生成的菜单 简单程序 xff1a
  • Linux同时使用无线和有线网络

    有时候我们可能同时插入了无线网卡和有线网络 xff0c 但是默认是通过有线网络连接的外网 如果同时需要使用无线网络连接外网以及使用有线网络进行高速局域网连接的话需要将默认网关设置成无线网络 xff1a 使用ip route show分别查看
  • 【Python】计算两个日期相差天数

    Python 计算两个日期相差天数
  • 《Zoom to learn Learn to zoom》学习笔记

    创新点 xff1a 使用真实原始LR和HR图像作为数据进行训练网络 xff0c 区别于其他大部分网络 xff08 用HR图和经过HR图进行下采样的LR图作为网络数据进行训练 xff09 采用由CX loss 改进的CoBi loss作为网络
  • cmake安装以及更新

    直接install cmake wget https cmake org files v3 6 cmake 3 6 2 tar gz span class token function tar span zxvf cmake 3 6 2 t
  • manjaro安装以及配置、安装输入法、向日葵、anaconda、pycharm、QQ

    为什么不用Ubuntu xff0c 用manjaro就不说了 xff0c 直接上步骤吧 xff01 1 做个优盘 找个优盘 xff0c 最好是3 0接口吧 xff0c 我用的2 0有点慢 先下载系统 xff0c 找个自己喜欢的风格 Manj
  • Python编程中的常见语句

    4 1 if条件判断语句 4 1 1 if条件判断语句单分支 单分支格式 xff1a if 判断条件 xff1a 语句块1 else xff1a 语句块2 例 xff1a name 61 input 39 请输入您的用户名 39 if na

随机推荐

  • Linux下创建新用户及用户权限

    一 用户创建 增加用户 1 在root权限下 xff1b 命令 xff1a useradd 43 用户名 xff0c 它不会在 home目录下创建同名文件夹 xff0c 也没有创建密码 xff0c 因此利用这个用户登录系统 xff0c 是登
  • cacert.pem是怎么来的

    小弟最近在搞支付宝支付接口 xff0c 碰到个问题 xff0c help 我看demo中有下面一行代码 xff1a PHP code 1 2 3 ca证书路径地址 xff0c 用于curl中ssl校验 请保证cacert pem文件在当前文
  • mapping文件的编写

    mapping文件的编写 xff08 以及实体类与xml中类型的对应关系 xff09 2017年02月18日 11 31 36 转角人生 阅读数 xff1a 2918 标签 xff1a mapping文件 更多 个人分类 xff1a map
  • java-兔子繁殖问题

    題目 xff1a 古典问题 xff1a 有一对兔子 xff0c 从出生后第3个月起每个月都生一对兔子 xff0c 小兔子长到第三个月后每个月又生一 对兔子 xff0c 假如兔子都不死 xff0c 问每个月的兔子总数为多少 xff1f 64
  • Nginx服务器的安装部署和框架简介

    Nginx服务器的安装部署 1 如何获取Nginx服务器安装文件 Nginx服务器的软件版本包括 Windows版 和 Linux版俩种 官网下载地址为http nginx org en download html 网页上提供了Nginx服
  • error - problem conecting

    解决的办法是在树莓派里安装如下模块 xff1a sudo apt get install tightvncserver 接着reboot重启 xff0c 重新连接即可
  • SQL 日期函数应用实例!!

    sql view plain copy select convert varchar 10 getdate 120 只返回当前日期 xff0c 且为2012 12 12格式 xff08 最有用 xff09 sql view plain co
  • 使用docker-compose搭建Harbor私有仓库

    一 安装docker compose 1 下载docker compose的最新版本 curl L 34 https github com docker compose releases download 1 22 0 docker com
  • Zabbix安装部署

    一 服务端安装配置 1 环境检查 root 64 m01 cat etc redhat release CentOS Linux release 7 4 1708 Core root 64 m01 uname r 3 10 0 693 el
  • Docker安装部署

    一 安装 devicemapper 存储驱动 使用说明 xff1a 为了在生产级别的环境中使用 docker 运行环境 xff0c 必须使用 direct lvm 模式来运行devicemapper 存储驱动 这种模式使用块设备来创建 th
  • MySql安装部署

    下载并安装MySQL官方的 Yum Repository wget i c http dev mysql com get mysql57 community release el7 10 noarch rpm 使用上面的命令就直接下载了安装
  • Nginx安装部署

    Nginx 安装配置 Nginx 34 engine x 34 是一款是由俄罗斯的程序设计师Igor Sysoev所开发高性能的 Web和 反向代理 服务器 xff0c 也是一个 IMAP POP3 SMTP 代理服务器 在高连接并发的情况
  • Rancher安装部署

    直接通过docker镜像来运行我们的rancher xff0c 首先 xff0c 先从镜像中心下载rancher镜像 xff0c 如果是1 x系列的 xff0c 镜像名为rancher server xff0c 而2 x是rancher r
  • form表单提交onclick和onsubmit进行表单验证

    onsubmit只能表单上使用 提交表单前会触发 onclick是按钮等控件使用 用来触发点击事件 在提交表单前 xff0c 一般都会进行数据验证 xff0c 可以选择在submit按钮上的onclick中验证 也可以在onsubmit中验
  • iperf3网络测试工具

    一 iperf能用来做什么 测量网络带宽和网络质量提供网络延迟抖动 数据包丢失率 最大传输单元等统计信息 二 iperf3主要功能介绍 TCP 测试网络带宽支持多线程 xff0c 在客户端与服务端支持多重连接报告MSS MTU值的大小支持T
  • C++多线程5-单例模式详解

    单例模式 xff1a 只允许创建一个类对象 xff0c 实现的关键是将构造函数变为私有 单例模式有几种实现方式 xff1a 懒汉模式饿汉模式线程安全模式 锁实现和call once实现 局部静态变量模式 1 懒汉模式 当需要使用类对象时 x
  • c++多线程1-多线程的创建

    什么是多线程 xff1f 我们可以理解为一个线程执行一个代码段 xff0c 所以多个线程就是执行多个代码段 xff0c 如果当一个线程结束后 xff0c 进程就退出了 xff0c 这个线程我们称之为主线程 每个进程可以有一个或一个以上的线程
  • c++多线程2-线程参数传递需要注意的几个问题

    一 线程的初始化参数需要注意以下几个问题 xff1a 1 回调函数使用引用参数接收值时 xff0c 必须声明为const xff0c 否则报错 xff1b xff08 线程基于数据安全保护的考虑 xff09 2 回调函数必须声明为指针 xf
  • c++11-智能指针

    c 43 43 智能指针 为了更安全地管理动态内存 xff0c c 43 43 11引入了智能指针 xff0c 提供了包括shared ptr unique ptr weak ptr三种不同类型的智能指针 目录结构 xff1a 一 三种指针
  • C++多线程3-共享数据操作保护

    目录 xff1a 1 多线程操作共享数据引出的问题 2 解决多线程操作共享数据的方法 xff1a 锁 3 互斥量mutex的概念和用法 4 lock普通锁的用法 5 lock guard类模板的用法 6 死锁的概念和解决 7 unique