当前位置: 首页 > news >正文

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);
}

 生产者与消费者模型

生产者与消费者模型icon-default.png?t=N176http://t.csdn.cn/GvZlZ

相关文章:

Linux(十)线程安全 上

目录 一、概念 二、互斥锁实现互斥 三、条件变量实现同步 银行家算法 生产者与消费者模型 一、概念 概念&#xff1a;在多线程程序中&#xff0c;如果涉及到了对共享资源的操作&#xff0c;则有可能会导致数据二义性&#xff0c;而线程安全就指的是&#xff0c;就算对共享…...

CRM系统能给企业带来什么? CRM系统推荐

什么是CRM系统&#xff1f; CRM系统&#xff08;又称客户关系管理系统&#xff09;是一个以客户为核心的管理软件&#xff0c;能有效改善企业与现有客户的关系&#xff0c;且帮助企业寻找新的潜在客户&#xff0c;并赢回以前老客户。 CRM系统能给企业带来什么&#xff1f; 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&#xff08;Resource Access Manage&#xff09;的子账号&#xff0c;然后可以获得Accesskey和Acess Secret)3.根据创建的子账号分配OSS的所有权限(可以对文件进行上传&…...

4年功能测试经验,裸辞后找不到工作怎么办?

软件测试四年&#xff0c;主要是手动测试&#xff08;部分自动化测试和性能测试&#xff0c;但是用的是公司内部自动化工具&#xff0c;而且我自动化方面是弱项。&#xff09; 现在裸辞三个月了&#xff0c;面试机会少而且面试屡屡受挫。总结就是自动化&#xff0c;性能&#…...

类和对象(中)(二)

类和对象&#xff08;中&#xff09;&#xff08;二&#xff09;1.赋值运算符重载1.1运算符重载1.2赋值运算符重载1.3前置和后置重载2.const成员3.取地址及const取地址操作符重载&#x1f31f;&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f;&#x1f31f;…...

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毕业论文选题管理系统

在分析并得出使用者对程序的功能要求时&#xff0c;就可以进行程序设计了。如图展示的就是管理员功能结构图。 系统实现前端技术&#xff1a;nodejsvueelementui 前端&#xff1a;HTML5,CSS3、JavaScript、VUE 系统分为不同的层次&#xff1a;视图层&#xff08;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&#xff09; quartz的不足 1.5、同类型框架对比 1.6 下载 1.6.1 文档地址 1.7 环境 二、XXL-JOB安装部署 2.1、配置部署“调度中心” 1&…...

通过 指针 引用 多维数组 详解

目录 一&#xff1a;回顾多维数组地址知识 二&#xff1a;二维数组的有关指针 三&#xff1a;指向数组元素的指针变量 四&#xff1a;用指向数组的指针作为函数参数 首先简单来讲&#xff0c;指针变量可以指向一维数组中的元素&#xff0c;也可以指向多维数组中的元素。下面…...

【Linux】宝塔面板 SSL 证书安装部署

宝塔面板 SSL 证书安装部署前言证书下载宝塔配置SSL注意事项前言 前期有讲过Tomcat和Nginx分别部署SSL证书&#xff0c;但也有好多小伙伴们私信我说&#xff0c;帮忙出一期宝塔面板部署SSL证书的教程&#xff0c;毕竟宝塔的用户体量也是蛮大的&#xff0c;于是宠粉的博主&…...

由 GPT 驱动的沙盒,尽情发挥想象力! #NovelAI

一个由 GPT 驱动的沙盒&#xff0c;供用户尽情发挥想象力的空间&#xff0c;会获得怎样的体验&#xff1f;NovelAI NovelAI 是一项用于 AI 辅助创作、讲故事、虚拟陪伴的工具。NovelAI 的人工智能算法会根据用户的方式创建类似人类的写作&#xff0c;使任何人&#xff0c;无论能…...

ubuntu 服务器安装配置VNC访问

ubuntu 如果服务器没有桌面相关图形包&#xff0c;需手动安装下&#xff1a; sudo apt install ubuntu-desktop sudo apt install lightdm VNC安装&#xff1a; 1.安装 在Ubuntu上安装x11vnc&#xff0c;如下&#xff1a; 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&#xff0c;CV甚至CG领域的流行&#xff0c;注意力机制&#xff08;Attention Mechanism&#xff09;被越来越多的学者所注意&#xff0c;将…...

设置Visual Studio 2022背景图

前言 编写代码时界面舒服&#xff0c;自己喜欢很重要。本篇文章将会介绍VS2022壁纸的一些设置&#xff0c;主题的更改以及如何设计界面。 理想的界面应该是这样的 接下来我们来一步步学习如何将界面设计成这样 一、壁纸插件下载 1.拓展->点击拓展管理 2.右上角搜索backgro…...

1. 用Qt开发的十大理由

用Qt的十大理由 原因最主要的是很多大公司都在用&#xff0c;有钱景。 先来看看各大公司的评价&#xff1a; 奔驰&#xff1a;们用 Qt 开发了绝大部分的UI体验和软件&#xff0c;包括屏幕动画&#xff0c;屏幕间的过渡和小组件。 FORMLABS&#xff1a;凭借Qt的快速迭代&…...

俄罗斯方块游戏代码

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a;小刘主页 ♥️每天分享云计算网络运维课堂笔记&#xff0c;努力不一定有收获&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️夕阳下&#xff0c;是最美的&#xff0c;绽…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

MongoDB学习和应用(高效的非关系型数据库)

一丶 MongoDB简介 对于社交类软件的功能&#xff0c;我们需要对它的功能特点进行分析&#xff1a; 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具&#xff1a; mysql&#xff1a;关系型数据库&am…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解

【关注我&#xff0c;后续持续新增专题博文&#xff0c;谢谢&#xff01;&#xff01;&#xff01;】 上一篇我们讲了&#xff1a; 这一篇我们开始讲&#xff1a; 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下&#xff1a; 一、场景操作步骤 操作步…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件

在选煤厂、化工厂、钢铁厂等过程生产型企业&#xff0c;其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进&#xff0c;需提前预防假检、错检、漏检&#xff0c;推动智慧生产运维系统数据的流动和现场赋能应用。同时&#xff0c;…...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

Leetcode 3577. Count the Number of Computer Unlocking Permutations

Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接&#xff1a;3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯&#xff0c;要想要能够将所有的电脑解锁&#x…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心

当仓库学会“思考”&#xff0c;物流的终极形态正在诞生 想象这样的场景&#xff1a; 凌晨3点&#xff0c;某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径&#xff1b;AI视觉系统在0.1秒内扫描包裹信息&#xff1b;数字孪生平台正模拟次日峰值流量压力…...

高防服务器能够抵御哪些网络攻击呢?

高防服务器作为一种有着高度防御能力的服务器&#xff0c;可以帮助网站应对分布式拒绝服务攻击&#xff0c;有效识别和清理一些恶意的网络流量&#xff0c;为用户提供安全且稳定的网络环境&#xff0c;那么&#xff0c;高防服务器一般都可以抵御哪些网络攻击呢&#xff1f;下面…...

如何理解 IP 数据报中的 TTL?

目录 前言理解 前言 面试灵魂一问&#xff1a;说说对 IP 数据报中 TTL 的理解&#xff1f;我们都知道&#xff0c;IP 数据报由首部和数据两部分组成&#xff0c;首部又分为两部分&#xff1a;固定部分和可变部分&#xff0c;共占 20 字节&#xff0c;而即将讨论的 TTL 就位于首…...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)

目录 一、&#x1f44b;&#x1f3fb;前言 二、&#x1f608;sinx波动的基本原理 三、&#x1f608;波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、&#x1f30a;波动优化…...