线程的同步互斥

 互斥

互斥保证了在一个时间内只有一个线程访问一个资源。

先看一段代码:三个线程同时对全局变量val进行--,同时val每自减一次其线程局部存储的全局变量++

#include <iostream>
#include <thread>
#include <vector>
#include <unistd.h>
#define NUM 3//线程数量
using namespace std;int val = 10;
__thread int t_val = 0;void fun(string name)
{while(1){if(val > 0){usleep(10000);cout << name << " get one" << " total: " << t_val << " val: " << val <<endl;val--;t_val++;}else {break;}}}int main()
{thread trd[NUM];//创建多个线程for(int i = 0; i < NUM; i++){string name = "thread" + to_string(i+1);trd[i] = thread(fun, name);}//等待线程for(int i = 0; i < NUM; i++){trd[i].join();}return 0;
}

最终的结果val竟然出现了负数,为什么呢?

因为线程函数的操作是非原子的,线程可能在任何一步被切换。这就会导致数据不一致问题,要想解决就必须使用锁。

锁——mutex

mutex是无参构造, 不支持拷贝构造。

两个无参接口,用来加锁和解锁 

#include <iostream>
#include <unistd.h>
#include <thread>
#include <vector>
#include <mutex>
#define NUM 3 // 线程数量
using namespace std;int val = 10;
thread_local int t_val = 0;
mutex mut;void fun(string name)
{while (1){{mut.lock();if (val > 0){usleep(10000);t_val++;val--;cout << name << " get one" << " total: " << t_val << " val: " << val << endl;mut.unlock();}else{mut.unlock();break;}}usleep(2);}
}int main()
{thread trd[NUM];// 创建多个线程for (int i = 0; i < NUM; i++){string name = "thread" + to_string(i + 1);trd[i] = thread(fun, name);}// 等待线程for (int i = 0; i < NUM; i++){trd[i].join();}return 0;
}

加锁保证锁内资源的数据安全性,是一种用时间换取安全的方式。锁在任何时候只能被一个线程申请,那么临界资源就只能被一个线程使用和修改,保证了数据一致性。

锁的分配也要合理,不然会导致线程的饥饿问题。如果线程在释放锁后立马申请锁就会导致其他线程无法获取锁

锁的原理

lock不是原子的,先将立即数0放到寄存器中,再将寄存器内容和内存中的mutex内容交换,最后判断寄存器内容。虽然lock不是原子的,不过即使发生进程切换,也不会发生错误。

锁未被申请时,mutex值为1。被申请后会将1传递到寄存器中,所以锁就是一种传递,哪个线程传递1到寄存器就可以执行

unlock是原子的,将mutex值置为1。

同步

同步可以在数据安全的情况下,让线程按照特定的顺序访问临界资源。

条件变量——condition_variable

 wait 可以让线程停止并等待通知,直到其他线程用notify接口发出通知,线程才会被唤醒。

使用 wait 必须搭配 std::unique_lock<std::mutex> 一起使用 

notify_one:通知一个正在等待的线程 

notify_all:通知所有正在等待的线程

例子:

通过等待,判断临界资源是否准备就绪,决定线程是否继续执行。

#include <iostream>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <unistd.h>using namespace std;
mutex m;
condition_variable cond;
bool flag = false;void fun()
{unique_lock<mutex> ul(m);cout << "已加锁,即将等待" << endl;while(flag != true){cond.wait(ul);cout << "结束等待,判断flag条件" << endl;}cout << "执行任务。。。" << endl;
}int main()
{thread th(fun);sleep(1);cond.notify_one();sleep(1);flag = true;cond.notify_one();th.join();cout << "end" << endl;return 0;
}

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

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

相关文章

Stable Diffusion WebUI本地环境搭建

一、项目代码下载 git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui 二、环境配置 conda create --n stafu python3.10.6 实际上跟自己创建的环境没有关系&#xff0c;项目启动会自动复制这个环境&#xff0c;之后项目根据这个基础环境构建 也可以在自己…

C++高性能通信:图形简述高性能中间件Iceoryx

文章目录 1. 概述2. 支持一个发布者多个订阅者2.2 Iceoryx为何不支持多个发布者发布到同一个主题 3. Iceoryx的架构和数据传输示意图3.1 发布者与订阅者的通信机制3.2 零拷贝共享内存通信机制 4. 使用事件驱动机制4.1 WaitSet机制4.2 Listener机制 5. 已知限制6. 参考 1. 概述 …

ImportError: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.20‘ 报错解决办法

1.查找 libstdc.so.6* find / -name libstdc.so.6*2.copy一个libstdc.so.6.0.19到/usr/lib64/下 cp /usr/lib64/libstdc.so.6 /usr/lib64/3.创建软连接 ln -sf /usr/lib64/libstdc.so.6.0.31 /usr/lib64/libstdc.so.6完毕&#xff01;

中电金信:云原生时代IT基础设施管理利器——基础设施即代码(IaC)

在数字化转型、零售业务快速发展、信创建设驱动下&#xff0c;应用架构、技术架构、基础架构都已向云原生快速演进&#xff0c;银行业IT基础设施管理产生了非常大的变化&#xff0c;当前银行业&#xff0c;正在开展新一轮的核心应用系统重构、基础平台统一建设等重点任务&#…

Linux网络:传输层协议TCP(二)三次挥手四次握手详解

目录 一、TCP的连接管理机制 1.1三次握手 1.2四次挥手 二、理解 TIME_WAIT 状态 2.1解决TIME_WAIT 状态引起的 bind 失败的方法 三、理解CLOSE_WAIT状态 一、TCP的连接管理机制 在正常情况下, TCP 要经过三次握手建立连接, 四次挥手断开连接 1.1三次握手 三次握手顾名思…

【设计模式】代理模式详解

1.简介 代理模式是常用的Java设计模式&#xff0c;该模式的特点是代理类与委托类共享相同的接口。代理类主要负责预处理消息、过滤消息、将消息转发给委托类&#xff0c;并在事后处理消息等。代理类与委托类之间通常存在关联关系&#xff0c;一个代理类对象与一个委托类对象关…

SpringBoot Mysql->达梦8 activiti6.0.0 项目迁移

全部源码&#xff1a;公众号搜索资小库&#xff0c;回复dm获取源码 1.整合达梦 1.1 达梦驱动下载 MyBatis-Plus 框架 | 达梦技术文档 (dameng.com) 1.2 数据迁移 怎么安装数据库&#xff0c;很多大佬有帖子&#xff0c;搜一下达梦先建立用户&#xff0c;使用DM管理工具 链…

SQL Server 数据误删的恢复

在日常的数据库管理中&#xff0c;数据的误删操作是难以避免的。为了确保数据的安全性和完整性&#xff0c;我们必须采取一些措施来进行数据的备份和恢复。本文将详细介绍如何在 SQL Server 中进行数据的备份和恢复操作&#xff0c;特别是在发生数据误删的情况下。假设我们已经…

使用visual studio编译C++项目时无法找到 enum中的某些项

vs 2017 编译一个cocos2dx 的老项目时&#xff0c;报错&#xff1a; 在项目中搜索关键字 ARMATURE_LOOP_COMPLETE&#xff0c;发现在文件EventType.h中是有定义的&#xff0c;是 enum Event 的一项&#xff0c;而且确认了报错的文件已经引入了这个头文件&#xff1a; 这太奇怪了…

傻瓜式PHP-Webshell免杀学习手册,零基础小白也能看懂

项目描述 一、PHP相关资料 PHP官方手册&#xff1a; https://www.php.net/manual/zh/ PHP函数参考&#xff1a; https://www.php.net/manual/zh/funcref.php 菜鸟教程&#xff1a; https://www.runoob.com/php/php-tutorial.html w3school&#xff1a; https://www.w3school…

【React】全面解析:从基础知识到高级应用,掌握现代Web开发利器

文章目录 一、React 的基础知识1. 什么是 React&#xff1f;2. React 的基本概念3. 基本示例 二、React 的进阶概念1. 状态&#xff08;State&#xff09;和属性&#xff08;Props&#xff09;2. 生命周期方法&#xff08;Lifecycle Methods&#xff09;3. 钩子&#xff08;Hoo…

Spring Cloud微服务项目统一封装数据响应体

在微服务架构下&#xff0c;处理服务之间的通信和数据一致性是一个重要的挑战。为了提高开发效率、保证数据的一致性及简化前端开发&#xff0c;统一封装数据响应体是一种非常有效的实践。本文博主将介绍如何在 Spring Cloud 微服务项目中统一封装数据响应体&#xff0c;并分享…

ValueError: invalid literal for int() with base 10: ‘a‘

ValueError: invalid literal for int() with base 10: ‘a‘ 目录 ValueError: invalid literal for int() with base 10: ‘a‘ 【常见模块错误】 【解决方案】 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff…

基于web3区块链的名酒资产数字化、个人闲置资产收藏系统,实现联盟链、NFT数据上链、智能合约开发

系统背景&#xff1a; 国内有众多历史悠久却极具收藏价值的名酒品类&#xff0c;但是传统名酒投资存在着保真、流通和收藏三大痛点&#xff0c;极大影响了名酒产业的发展。基于区块链的分布式、不可篡改、可追溯、透明性、多方维护、交叉验证等特性&#xff0c;数据权属可以被有…

【Linux】软连接|硬链接|当前路径(.)|上级路径(..)|硬链接不能链接目录

目录 前言 软连接 ​编辑 删除源文件 快捷应用 总结 硬链接 硬链接为何不能链接目录 为什么软连接可以 软硬链接区别 当前路径(.)和上级路径(..) ​编辑 前言 在 Linux 中&#xff0c;文件的存储位置和数据&#xff08;属性内容&#xff09;是由 inode 号来唯一标…

错误:请查看是否设备未加入到证书列表或者确认证书类型是否匹配

这个问题实际上网上都有解法&#xff0c;但是可能没有那么的清楚&#xff0c;大家在各种问&#xff0c;我既然搞定了&#xff0c;就分享给大家吧网上解法&#xff1a; 开发调试需要另外创建开发证书和描述文件&#xff0c;描述文件同时绑定开发设备解读&#xff1a; 实际上这句…

electron 主进程和渲染进程

最近在整理electron 相关的项目问题&#xff0c;对自己来说也是温故知新&#xff0c;也希望能对小伙伴们有所帮助&#xff0c;大家共同努力共同进步。加油&#xff01;&#xff01;&#xff01;&#xff01; 虽然最近一年前端大环境不好&#xff0c;但是大家还是要加油鸭&#…

SmartInitializingSingleton和InitializingBean的区别

SmartInitializingSingleton&#xff1a;接口里面就一个方法afterSingletonsInstantiated&#xff0c;它是spring容器将所有bean都初始化完成之后&#xff0c;才会去调用&#xff0c;要求实现它接口的bean必须是单例的。 应用场景&#xff1a;可以在服务启动之后去处理一些逻辑…

科普文:从源码解读5种Redis基本数据类型

键值对字符串 char* 与 SDS char* 的不足&#xff1a; 操作效率低&#xff1a;获取长度需遍历&#xff0c;O(N)复杂度 二进制不安全&#xff1a;无法存储包含 \0 的数据 SDS 的优势&#xff1a; 操作效率高&#xff1a;获取长度无需遍历&#xff0c;O(1)复杂度&#xff08…

60个常见的 Linux 指令

常见60个Linux指令 1.ssh 登录到计算机主机2.ls 列出目录内容3.pwd 当前终端会话所在的完整路径4.cd 切换当前工作目录5.touch 创建空文件或更新文件的时间戳6.echo 终端输出文本或变量值7.nano 在终端中编辑文件8.vim 文本编辑器9.cat 查看、连接和创建文件10.shred 安全删除敏…