Linux(十)线程安全 上
目录
一、概念
二、互斥锁实现互斥
三、条件变量实现同步
银行家算法
生产者与消费者模型
一、概念
概念:在多线程程序中,如果涉及到了对共享资源的操作,则有可能会导致数据二义性,而线程安全就指的是,就算对共享资源进行操作也不会导致数据二义
实现:如何实现多线程中对共享资源的操作不会出问题
互斥:通过同一时间对资源访问的唯一性,保证访问安全
互斥的实现:互斥锁(读写锁、自旋锁...)
同步:通过条件控制,让多执行对资源的获取更加合理
同步实现:条件变量、信号量
二、互斥锁实现互斥
实现对共享资源的唯一访问
1)互斥锁实现互斥的原理
本质:就是一个1/0计数器,通过0/1标记资源的访问状态(0-不可访问、1-可访问)
在访问资源之前进行加锁(通过状态判断是否可访问,不可访问则阻塞)
在访问资源之后进行解锁(将资源状态置为可访问状态,唤醒其他阻塞的线程)
也有另一种理解:访问资源之前加锁(获取锁资源-获取不到就阻塞)
访问完毕解锁(归还锁资源)
多个线程想要实现互斥,就必须访问同一个锁才可以,也就意味着锁也是一个共享资源
互斥锁的操作本身必须是安全的:互斥锁本身计数器的操作时原子操作
2)互斥锁如何实现自身操作安全的原理
我们知道内存与cpu之间的数据传输:当进行加锁操作时,先将锁中的1置入cpu(先将变量数据从内存加载到cpu)然后才能从cpu中对数据进行处理(转化为0)然后在从cpu中将数据加载到内存指定位置(完成将1替换为0)
然而这样一种操作就会产生问题,如果1从内存加载到cpu还没有将处理后的0加载回内存时,这时切换其他线程运行访问到的锁资源仍然为1,就会继续进行加锁,这无疑是致命的。
所以对于锁资源本身来说,计数器的操作必须是原子性的才可以。
有个指令类似于exchange,功能是交换指定寄存器与内存中的数据
1、先将指定寄存器中的值修改为0,
2、将寄存器与内存中的数据进行互换
3、判断是否符合获取锁的条件或者说判断是否能够加锁
这样使用exchange指令就可以实现计数器操作为原子操作。
3)接口
互斥锁类型变量 pthread_mutex_t
初始化互斥锁
int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *attr)
mutex:互斥锁变量的地址 attr:互斥锁变量属性(通常置NULL)
访问资源前加锁
int pthread_mutex_lock(pthread_mutex_t *mutex) 阻塞加锁(老实人)
int pthread_mutex_trylock(pthread_mutex_t *mutex) 非阻塞加锁(海王)
访问资源后解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex)
释放销毁互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex)
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER (这种初始化不需要销毁)
4)代码模拟
当不使用互斥锁线程之间对同一变量的访问情况如何?
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>int ticket = 100;
void *Scalper(void *arg)
{while(1){if(ticket > 0){usleep(10);printf("%p:我抢到了第 %d 号票\n",pthread_self(), ticket);ticket--;}else{printf("%p:票完了,我的工作结束了\n", pthread_self());break;}}return NULL;
}
int main()
{pthread_t tid[4];for(int i = 0; i < 4; i++){int ret = pthread_create(&tid[i], NULL, Scalper, NULL);if(ret != 0){perror("create error");return -1;}}for(int i = 0; i < 4; i++){pthread_join(tid[i], NULL);}return 0;
}
发现出现了很多意外情况,重复、负数票号,
为什么呢?就是因为每个线程之间访问同一变量,一个线程还正对变量进行操作的时候另一个线程也进行操作,于是就发生了同一变量的多次出现。
使用互斥锁保护临界区
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>int ticket = 100;
void *Scalper(void *arg)
{while(1){pthread_mutex_lock(arg); // 访问之前加锁if(ticket > 0){usleep(10);printf("%p:我抢到了第 %d 号票\n",pthread_self(), ticket);ticket--;pthread_mutex_unlock(arg); // 在所有有可能退出的地方解锁}else{printf("%p:票完了,我的工作结束了\n", pthread_self());pthread_mutex_unlock(arg); // 在所有有可能退出的地方解锁break;}usleep(1);}return NULL;
}
int main()
{pthread_mutex_t mutex; // 定义锁变量 mutexpthread_mutex_init(&mutex, NULL); // 初始化锁资源pthread_t tid[4];for(int i = 0; i < 4; i++){int ret = pthread_create(&tid[i], NULL, Scalper, &mutex); // 注意锁资源通过线程创建第四个参数传入入口函数内if(ret != 0){perror("create error");return -1;}}for(int i = 0; i < 4; i++){pthread_join(tid[i], NULL);}return 0;
}
5)死锁
死锁是一种状态,是一种因为资源争抢不当导致程序流程卡死无法继续向下推进的状态。
多个线程对锁资源的争抢使用不当导致程序流程卡死,无法继续向下推进的状态
1、加锁之后没有释放就退出,导致其他线程获取不到锁资源卡死
2、多锁使用时,加锁顺序不当,线程1加锁顺序为AB,线程2加锁顺序为BA
发生死锁的必要条件
① 互斥条件 同一时间一把锁只能被一个线程所获取到
② 不可剥夺条件 一个线程加的锁,只能自己释放,其他线程无法释放
③ 请求与保持 一个线程请求了A锁之后请求B锁,如果请求不到就不会释放A锁
④ 环路等待 线程1加了A锁后请求B锁,线程2加了B锁后请求A锁
死锁的预防:破坏死锁产生的必要条件
① 和 ② 无法修改
写代码时要注意:
1、线程之间的加解锁顺序尽量一致 -- 尽可能预防环路等待
2、采用非阻塞加锁,如果加不上锁,则把已经加上的锁释放 -- 破坏请求与保持
(请求不到新的,则释放已有的)
避免:银行家算法、死锁检测算法……
银行家算法http://t.csdn.cn/1YxZj
三、条件变量实现同步
1)概念
同步:通过条件控制,保证资源访问的合理性
条件变量:主要是一个pcb等待队列,以及唤醒和阻塞线程的接口
原理:
线程1 获取资源时进行判断,如果线程不符合资源获取条件,则调用阻塞接口进行阻塞
线程2 促使资源获取条件满足之后(生产资源),通过唤醒接口唤醒阻塞的线程
条件变量需要搭配互斥锁来使用
举例:
顾客 与 厨师 (消费者与生产者)
顾客来到柜台,看到柜台有饭则吃饭,否则阻塞
厨师来到柜台,看到柜台上没有饭则做饭,否则阻塞
顾客:
0、加锁(关门)
1、访问柜台有没有饭
有饭则吃饭,没有饭就阻塞
(这里阻塞时需要进行解锁,否则厨师就无法访问柜台,也就无法做饭,产生死锁)
阻塞则需先解锁,再阻塞,被唤醒之后再加锁
2、吃饭
3、吃完了,再来一碗 唤醒厨师
4、解锁
厨师:
0、加锁
1、访问柜台有没有饭
没饭则做饭,有饭则阻塞
(这里是有饭就需要解锁,让顾客能够吃饭,否则会死锁)
阻塞需先解锁,再阻塞,被唤醒后加锁
2、做饭
3、做好了,唤醒顾客
4、解锁
2)接口
定义条件变量:
pthread_cond_t 条件变量的变量类型
初始化条件变量:
pthread_cond_t cond_init (pthread_cond_t *cond, pthread_condattr_t *attr);
阻塞接口:条件变量是搭配互斥锁一起使用的,就体现在阻塞这一步
int pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex) -- 阻塞接口
int pthread_cond_timewait(pthread_cond_t *cond, pthread_mutex_t *mutex,
struct timespec *t ) -- 有时长限制的阻塞
唤醒接口:
int pthread_cond_signal(pthread_cond_t *cond) 唤醒至少一个阻塞队列中的线程
int pthread_cond_broadcast(pthread_cond_t *cond) 唤醒等待队列中所有的线程
销毁接口:
int pthread_cond_destroy(pthread_cond_t *cond)
3)模拟实现
#include<stdio.h>
#include<pthread.h>int counter = 0; // 定义柜台状态 0——没饭 1——有饭
pthread_mutex_t mutex; // 初始化锁
pthread_cond_t cond; // 初始化条件变量void* customer(void *arg)
{while(1){pthread_mutex_lock(&mutex); // 先加锁if(counter==0){pthread_cond_wait(&cond, &mutex); // 没饭则解锁 并阻塞,等待唤醒,唤醒后加锁}printf("真好吃,再来一碗\n");counter = 0;pthread_cond_signal(&cond); // 吃完了唤醒厨师做饭pthread_mutex_unlock(&mutex); // 解锁}
}void* cook(void *arg)
{while(1){pthread_mutex_lock(&mutex); // 加锁if(counter == 1){pthread_cond_wait(&cond, &mutex); // 有饭则 解锁并阻塞,等待唤醒,唤醒后加锁}printf("你的饭好了\n");counter = 1;pthread_cond_signal(&cond); // 饭做好了唤醒顾客吃饭pthread_mutex_unlock(&mutex); // 解锁}
}int main()
{pthread_mutex_init(&mutex, NULL); // 初始化定义mutex 和 condpthread_cond_init(&cond, NULL);pthread_t cook_tid; // 初始化定义线程ID(顾客和厨师)pthread_t cus_tid;int ret;ret = pthread_create(&cook_tid, NULL, cook, NULL); // 分别创建对应线程if(ret != 0){perror("create error");return -1;}ret = pthread_create(&cus_tid, NULL, customer, NULL);if(ret != 0){perror("create error");return -1;}pthread_join(cook_tid, NULL); // 执行线程等待pthread_join(cus_tid, NULL);pthread_mutex_destroy(&mutex); // 执行mutex与cond的销毁pthread_cond_destroy(&cond);
}
实现四个顾客四个厨师
在同步实现的代码中,如果存在多种角色,就应该定义多个条件变量,各自处于各自的pcb等待队列中。
#include<stdio.h>
#include<pthread.h>int counter = 0;
pthread_mutex_t mutex;
pthread_cond_t cond_cus; // 使用不同的条件变量,防止死锁
pthread_cond_t cond_cook;
void* customer(void *arg)
{while(1){pthread_mutex_lock(&mutex);while(counter <= 0){pthread_cond_wait(&cond_cus, &mutex);}printf("真好吃,再来一碗: %d\n",counter);counter--;pthread_cond_signal(&cond_cook);pthread_mutex_unlock(&mutex);}
}void* cook(void *arg)
{while(1){pthread_mutex_lock(&mutex);while(counter > 0){pthread_cond_wait(&cond_cook, &mutex);}printf("你的饭好了:%d\n", counter);counter++;pthread_cond_signal(&cond_cus);pthread_mutex_unlock(&mutex);}
}int main()
{pthread_mutex_init(&mutex, NULL);pthread_cond_init(&cond_cus, NULL);pthread_cond_init(&cond_cook, NULL);pthread_t cook_tid[4]; // 定义四个顾客、四个厨师pthread_t cus_tid[4];int ret;for(int i = 0; i < 4; i++) // 创建这八个线程{ret = pthread_create(&cook_tid[i], NULL, cook, NULL);if(ret != 0){perror("create error");return -1;}ret = pthread_create(&cus_tid[i], NULL, customer, NULL);if(ret != 0){perror("create error");return -1;}}pthread_join(cook_tid[0], NULL);pthread_join(cus_tid[0], NULL);pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond_cook);pthread_cond_destroy(&cond_cus);
}
生产者与消费者模型
生产者与消费者模型http://t.csdn.cn/GvZlZ
相关文章:
Linux(十)线程安全 上
目录 一、概念 二、互斥锁实现互斥 三、条件变量实现同步 银行家算法 生产者与消费者模型 一、概念 概念:在多线程程序中,如果涉及到了对共享资源的操作,则有可能会导致数据二义性,而线程安全就指的是,就算对共享…...
CRM系统能给企业带来什么? CRM系统推荐
什么是CRM系统? CRM系统(又称客户关系管理系统)是一个以客户为核心的管理软件,能有效改善企业与现有客户的关系,且帮助企业寻找新的潜在客户,并赢回以前老客户。 CRM系统能给企业带来什么? C…...
ESP32设备驱动-LED控制器生成PWM信号
LED控制器生成PWM信号 文章目录 LED控制器生成PWM信号1、LED控制器介绍2、软件准备3、硬件准备4、代码实现PWM 是一种在数字引脚上获取类似模拟信号的方法。PWM实际上是一个在高电平和低电平之间切换的方波信号,在 0V 和 3.3V 之间。 当信号为 HIGH 和 LOW 时,这种连续的 HIG…...
秒杀项目之网关服务限流熔断降级分布式事务
目录一、网关服务限流熔断降级二、Seata--分布式事务2.1 分布式事务基础2.1.1 事务2.1.2 本地事务2.1.3 分布式事务2.1.4 分布式事务场景2.2 分布式事务解决方案2.2.1 全局事务可靠消息服务2.2.2 最大努力通知2.2.3 TCC事事务三、Seata介绍四、 Seata实现分布式事务控制4.1 案例…...
OSS(Object Storage Service)进行上传图片,下载图片(详细看文档可以完成操作)
文章目录1.单体前后端项目上传1.上传流程2. BuckName 和EndPoint3. AccessKey 和Access Secret(创建RAM(Resource Access Manage)的子账号,然后可以获得Accesskey和Acess Secret)3.根据创建的子账号分配OSS的所有权限(可以对文件进行上传&…...
4年功能测试经验,裸辞后找不到工作怎么办?
软件测试四年,主要是手动测试(部分自动化测试和性能测试,但是用的是公司内部自动化工具,而且我自动化方面是弱项。) 现在裸辞三个月了,面试机会少而且面试屡屡受挫。总结就是自动化,性能&#…...
类和对象(中)(二)
类和对象(中)(二)1.赋值运算符重载1.1运算符重载1.2赋值运算符重载1.3前置和后置重载2.const成员3.取地址及const取地址操作符重载🌟🌟hello,各位读者大大们你们好呀🌟🌟…...
Hadoop自动安装JDK
目录 1、使用xftp工具 在opt目录下创建install和soft文件 2、使用xftp工具 将压缩包上传到install文件 3、编写shell脚本 3.1、创建目录来放shell脚本 3.2、创建autoinsatll.sh文件并修改权限 3.3、编写autoinsatll.sh 文件 4、 运行 5、测试 1、使用xftp工具 在opt目…...
Springboot+Vue java毕业论文选题管理系统
在分析并得出使用者对程序的功能要求时,就可以进行程序设计了。如图展示的就是管理员功能结构图。 系统实现前端技术:nodejsvueelementui 前端:HTML5,CSS3、JavaScript、VUE 系统分为不同的层次:视图层(vue页面&#…...
面向战场的cesium基础到进阶的案例展示(我相信VIP总是有原因的)
cesium 前置说明(友情提示,关注重点代码,其他影响复现的都可以删除或者替换数值解决) 这里面用到了cesium的模型加载、图片加载、着色器、实时改变模型状态、模型删除等知识点,这需要你自己去观摩下述会包含所有相关代码,他们的联系其实在代码中能看到(比如飞机操作类会…...
XXL-JOB 分布式任务调度平台
目录 一、简介 1.1 概述 1.2 社区交流 1.3 特性 1.4 架构设计 1.4.1 设计思想 1.4.2 系统组成 1.4.3 调度模块剖析 1) quartz的不足 1.5、同类型框架对比 1.6 下载 1.6.1 文档地址 1.7 环境 二、XXL-JOB安装部署 2.1、配置部署“调度中心” 1&…...
通过 指针 引用 多维数组 详解
目录 一:回顾多维数组地址知识 二:二维数组的有关指针 三:指向数组元素的指针变量 四:用指向数组的指针作为函数参数 首先简单来讲,指针变量可以指向一维数组中的元素,也可以指向多维数组中的元素。下面…...
【Linux】宝塔面板 SSL 证书安装部署
宝塔面板 SSL 证书安装部署前言证书下载宝塔配置SSL注意事项前言 前期有讲过Tomcat和Nginx分别部署SSL证书,但也有好多小伙伴们私信我说,帮忙出一期宝塔面板部署SSL证书的教程,毕竟宝塔的用户体量也是蛮大的,于是宠粉的博主&…...
由 GPT 驱动的沙盒,尽情发挥想象力! #NovelAI
一个由 GPT 驱动的沙盒,供用户尽情发挥想象力的空间,会获得怎样的体验?NovelAI NovelAI 是一项用于 AI 辅助创作、讲故事、虚拟陪伴的工具。NovelAI 的人工智能算法会根据用户的方式创建类似人类的写作,使任何人,无论能…...
ubuntu 服务器安装配置VNC访问
ubuntu 如果服务器没有桌面相关图形包,需手动安装下: sudo apt install ubuntu-desktop sudo apt install lightdm VNC安装: 1.安装 在Ubuntu上安装x11vnc,如下: sudo apt-get install x11vnc 2.配置vnc密码 x1…...
【C→C++】打开C++世界的大门
文章目录前言什么是CC的发展史C的重要性1. 使用广泛度2. 工作领域的应用1. C关键字(C98)2. 命名空间2.1 命名空间的定义2.2 命名空间的使用2.3 std命名空间的使用惯例3. C输入&输出3.1 输入输出3.2 说明4. 缺省参数4.1 缺省参数概念4.2 缺省参数分类5. 函数重载5.1 函数重载…...
点云深度学习系列博客(四): 注意力机制原理概述
目录 1. 注意力机制由来 2. Nadaraya-Watson核回归 3. 多头注意力与自注意力 4. Transformer模型 Reference 随着Transformer模型在NLP,CV甚至CG领域的流行,注意力机制(Attention Mechanism)被越来越多的学者所注意,将…...
设置Visual Studio 2022背景图
前言 编写代码时界面舒服,自己喜欢很重要。本篇文章将会介绍VS2022壁纸的一些设置,主题的更改以及如何设计界面。 理想的界面应该是这样的 接下来我们来一步步学习如何将界面设计成这样 一、壁纸插件下载 1.拓展->点击拓展管理 2.右上角搜索backgro…...
1. 用Qt开发的十大理由
用Qt的十大理由 原因最主要的是很多大公司都在用,有钱景。 先来看看各大公司的评价: 奔驰:们用 Qt 开发了绝大部分的UI体验和软件,包括屏幕动画,屏幕间的过渡和小组件。 FORMLABS:凭借Qt的快速迭代&…...
俄罗斯方块游戏代码
♥️作者:小刘在C站 ♥️个人主页:小刘主页 ♥️每天分享云计算网络运维课堂笔记,努力不一定有收获,但一定会有收获加油!一起努力,共赴美好人生! ♥️夕阳下,是最美的,绽…...
设计模式相关面试题
文章目录面向对象编程中,都有哪些设计原则?设计模式的分类 ?单例模式的特点是什么?单例模式有哪些实现?什么是简单⼯⼚模式什么是抽象⼯⼚模式?什么是⼯⼚⽅法模式?什么是代理模式?S…...
构建Jenkins 2.340持续集成环境
一、前言 本文学习自:2022版Jenkins教程(从配置到实战) 如有不妥,欢迎指正 二、构建资料 已经包括了本文档使用的所有所需的安装包 三、安装docker 1、解压docker docker-20.10.10.tgz2、复制文件 cp docker/* /usr/bin/3、编写启动文…...
Ubuntu/Centos下OpenJ9 POI输出Excel的Bug
项目更换 JDK为 OpenJ9 后, 使用 POI 导出 Excel 遇到的问题 OpenJ9 版本信息 /opt/jdk/jdk-11.0.178/bin/java -version openjdk version "11.0.17" 2022-10-18 IBM Semeru Runtime Open Edition 11.0.17.0 (build 11.0.178) Eclipse OpenJ9 VM 11.0.17.0 (build …...
链接脚本学习笔记
IAR 一般步骤 链接器用于链接过程。它通常执行以下过程(请注意,某些步骤可以通过命令行选项或链接器配置文件中的指令关闭): 1.确定应用程序中要包含哪些模块。始终包含对象文件中提供的模块。仅当库文件中的模块为从包含的模块…...
NLP顶会近三年小众研究领域
ACL 2022 编码器和解码器框架、自然语言生成、知识i神经元、抽取式文本摘要、预训练语言模型、零样本神经机器翻译等。 2021 新闻标题生成任务等。跨语言命名实体识别、代码搜索、音乐生成、Hi-Transformer、预训练语言模型、语义交互等。 EMNLP 2021 代码摘要生成、隐私…...
[electron] 一 vue3.2+vite+electron 项目集成
一 开发环境系统:windows开发工具: git , vscode,termial环境依赖: node, npm 二 步骤2.1 通过vite 创建vue项目通过 终端执行命令,选择 模板 vuenpm init vite cd 项目目录 npm install npm run dev2.2 集成 electro…...
ESP32 Arduino(十二)lvgl移植使用
一、简介LVGL全程LittleVGL,是一个轻量化的,开源的,用于嵌入式GUI设计的图形库。并且配合LVGL模拟器,可以在电脑对界面进行编辑显示,测试通过后再移植进嵌入式设备中,实现高效的项目开发。SquareLine Studi…...
js一数组按照另一数组进行排序
有时我们需要一个数组按另一数组的顺序来进行排序,总结一下方法,同时某些场景也会用到。 首先一个数组相对简单的情况: var arr1 [52,23,36,11,09]; var arr2 [23,09,11,36,52]; // 要求arr1按照arr2的顺序来排序,可以看到两个…...
JavaScript 类继承
JavaScript 类继承是面向对象编程的一个重要概念,它允许一个类从另一个类继承属性和方法。通过使用继承,可以避免代码重复,并可以在现有类的基础上扩展新功能。 在 JavaScript 中,您可以使用关键字 extends 来实现类继承。例如&a…...
MySQL相关面试题
文章目录union 和 unionAll 的区别?drop、delete与truncate的区别 ?sql 语句如何优化 ?什么是事务 ?事务的四个特性(ACID) ?事务的隔离级别?索引主要有哪几种分类 ?什么时候适合添加索引&#x…...
响应式网站 图片居中/昆明seo网站建设
leetcode 837. 新21点 题目详情 题目链接 爱丽丝参与一个大致基于纸牌游戏 “21点” 规则的游戏,描述如下: 爱丽丝以 0 分开始,并在她的得分少于 K 分时抽取数字。 抽取时,她从 [1, W] 的范围中随机获得一个整数作为分数进行累计…...
做第三方网站注意什么意思/搜索引擎排名优化方案
VMWare16 安装 Ubuntu16.04.7 1. 下载Ubuntu镜像 下载地址:http://mirrors.163.com/ubuntu-releases/ 本机是32位的话,就选择i386;本机是64位的话,就选择amd64 2. 安装步骤 3. 安装VMware Tools 启动虚拟机,在虚拟机管理界面…...
wordpress 鲜果/2022最新引流推广平台
[转载]原文出处:http://blog.csdn.net/lqhbupt/article/details/24737029相关Matlab函数:hist, bar, cdfplot, ksdensity(1) hist函数n hist(Y, x)如果x是一个向量,返回x的长度个以x为中心的,Y的分布情况。例如:如果x…...
平谷区建设委员会网站/南昌seo实用技巧
1.Redis名字的来源 全称是Remote Dictionary Server, 是一个高效能的key-value数据库 2.Redis的用途 用作缓存,存高频访问的数据,避免了频繁查询数据库 2.Redis支持5种数据类型 2.1 String(字符串) 一个键最大能存储512MB 2.2 Hash Redis Hash 是键值对集…...
wordpress 获取文章数量/域名查询
苹果几周前在WWDC 2021上发布了iOS 15,向开发者发布了第一个测试版。这通常是最有可能包含一些恼人的bug和影响电池寿命的iOS测试版,这也是苹果最初向开发者提供其软件测试版的原因。但到目前为止,iOS 15测试版1一直是一个伟大的、稳定的体验…...
帝国cms建设 政府网站/seo技术是什么意思
一、引言 Mybatis可以直接传入一个集合,通过标签将集合中的数据遍历出来实现批量新增 不同的数据库批量新增有区别,如下实现两种方式的批量新增 二、实现 2.1映射文件 <!--单条插入 --><insert id"insertOne" parameterType"…...