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

[Linux]线程的互斥与同步

目录

一、互斥

1.互斥的概念

2.互斥锁接口

3.线程加锁解锁本质

4.死锁

二、同步

1.同步的概念

2.条件变量

3.条件变量接口


一、互斥

1.互斥的概念

        互斥指的是任何时刻,互斥保证有且只有一个执行流进入临界区,进行临界资源的访问,通常对于临界资源起到保护作用的一种机制。

        在多线程下,访问全局变量的话,一个线程的修改会影响到其他线程,如果说一些操作不是原子的话,会有数据不一致的问题。例如,多线程下抢电影票的情况,本来有count:100张票,但是最后可能卖出去100多张,是因为count--操作不是原子的。

        当执行count--操作的时候,转化为汇编操作有3步,第一步是将count的值读取出来放入CPU寄存器中,第二步执行--操作,第三步将count的值覆盖会物理内存的位置,改变count在物理内存中的值。但是如果说在第二步执行完之后,该线程的时间片到了,就会从CPU上剥离下来,本来取出来的是10,在CPU中已经修改为9了,但是没有写回到内存中,所以内存的count值还是10,那么别的线程就会读取到count值是10,之后在CPU中在进行操作,并写回物理内存的值也为9。等到第一个修改为9的进程再次回到CPU中执行时,会执行第三步操作,将9有一次的写回到了物理内存当中。此时count的值是多少都不知道,可能已经减为0了,但是又被写回了9,所以说不是原子的操作在多线程下会有很大的问题。

        又或者是多线程下一个变量充当if判断条件的时候,可能刚读取到该变量,时间片就到了,没有来得及进行判断,就被剥离了下来,其他的线程继续操作,最终把变量改为了不满足if判断条件了,本来该线程应该判断后退出的,但是该线程已经取出了该条件变量,是最开始的值,所以还是会判断符合条件执行if内部代码,所以说也会出错的。

        数据在内存是被多线程共享的,但是一旦读取到寄存器当中,就变成了线程的上下文数据,就属于私有了。所以互斥就是为了解决上述的数据不一致问题,就是同一时间只让一个线程进行访问该资源。

2.互斥锁接口

        上述的问题可以使用互斥锁来解决,锁是用来保护临界区在同一时间只能由一个进程进行访问的。锁相当于一个资源,多线程去竞争该资源,但是只有一个线程可以获取到该资源,其他线程就会阻塞在锁的位置等待锁资源,获取到锁资源的线程才可以去访问临界区的资源。当线程执行完临界区的操作之后,释放锁,其他线程才可以继续竞争锁资源。

全局锁

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

创建局部锁

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);

        第一个参数是要初始化的锁对象,第二个参数是设置锁的属性,一般设置为nullptr即可。 

销毁局部锁

int pthread_mutex_destroy(pthread_mutex_t *mutex); 

加锁和解锁

int pthread_mutex_lock(pthread_mutex_t *mutex);

int pthread_mutex_unlock(pthread_mutex_t *mutex);

int pthread_mutex_trylock(pthread_mutex_t *mutex); 

        对于pthread_mutex_lock加锁是阻塞式的申请锁资源,如果没有申请到的话,则会阻塞等待锁资源,而pthread_mutex_trylock则是尝试申请锁资源,也就是非阻塞式的申请,如果有的话就申请到了,没有的话就不申请了,直接返回。

 

3.线程加锁解锁本质

        多线程去申请锁资源,锁保证了线程之间的互斥,但是锁也是共享资源啊,谁来保证锁的安全性呢?所以要和信号量一样,对于申请锁的操作设置为原子的。该原子的操作是基于硬件指令的支持,比如说比较并交换的CAS等指令就是一步执行的操作操作。把申请锁的操作转换为原子的汇编指令就保证了申请锁的原子性。

pthread_mutex_lock()接口的加锁实现:

lock:
    movb %0, %al
    xchgb %al, mutex
    if(al寄存器 > 0) return 0;
    else 挂起等待
    go to lock;

        首先将al寄存器的内容置为0,之后xchgb交换al寄存器和mutex标记位中的值,当有锁资源的时候mutex的标记位是1,没有的时候是0。xchagb就是一种原子性的操作指令,也就是swap操作。 交换之后判断是否申请到了锁资源,也就是判断al寄存器交换过来的数据是不是1,如果是1的话证明获取了锁资源就可以执行访问临界区的代码了,如果没有获取到,那么就会进入else阻塞的等待,如果有锁资源了,会跳转到lock开头重复上述动作进行申请锁资源。 

        当有一批线程申请该锁资源的时候,第一个进行swap交换的线程会将mutex的标记位交换位0,而1则是进入了该线程的al寄存器中,判断al寄存器大于0跳出循环,执行临界区代码。此时锁的标记位就是0了,其他线程来了之后,再怎么交换al寄存器中的值都是0,所以都需要进行阻塞等待。

        当线程调度切换的时候,也不会有影响,其他线程来的时候还是要阻塞等待,因为标志位1被申请到锁资源的线程通过切换上下文数据而带走了,只有等到该线程重新被CPU调度,执行完临界区代码,将标志位1交换会mutex,别的线程才能交换到标志位为1,才能让al寄存器大于0,从而获得到锁资源,返回执行临界区代码。

        也就是说锁资源的标志位,就相当于一把钥匙,也只要一把钥匙,那么一个人拿走该钥匙之后,不管什么情况,当这个人没用完之前,没把钥匙换回来之前,其他人都打不开这个门,都需要在门口进行等待。

pthread_mutex_unlock()接口的解锁实现:

unlock:

        movb $1, mutex

        唤醒阻塞等待的进程

        return 0;

        解锁的操作,就是将标志位的1交换会锁资源中,然后唤醒等待的线程去竞争锁资源。

4.死锁

        死锁指的是一组线程中,各个线程均占有不会被释放掉的资源,但又因相互申请被其他线程锁占用不会释放的资源而处于一种持久等待的状态,称之为死锁。例如下面的图所示,有两个资源A和B,1号线程占有A资源,2号线程占有B资源,而两者又互相想要对方的资源,所以都在互相等待。

        一个线程也可以产生死锁,可以让一个进程对同一个锁资源重复申请两次就变成了拥有了该资源还在申请该资源的情况,就变为了死锁。
        那么死锁如何避免呢?首先死锁的产生拥有四个条件:互斥条件、请求与保持条件、不剥夺条件、循环等待条件。请求与保持指的是一个执行流因请求资源而阻塞的时候,对以获得的资源保持不放,并且还在一直申请资源。循环等待指的是若干个执行流之间形成了一种头尾详解的循环等待资源的关系。

        解决死锁的方式就是破坏其中一个条件即可。例如:不适用锁;在申请锁资源不成功的时候,把自己拥有的资源释放掉;强行将别人的资源抢占过来;按照同样的顺序申请锁资源,就是对于多个资源的申请做一个先后顺序,先有A资源,才可以申请B资源,而不是所有资源都可以无条件的申请。

二、同步

1.同步的概念

        同步指的是在临界资源使用安全的情况下,让多线程的执行具有一定的顺序性,能够较为充分的利用资源。对于竞争锁资源谁竞争到,我们是无法控制的,但是我们可以通过一些方式来规定谁可以去竞争锁资源来实现线程访问临界资源具有一定的顺序性。

        
2.条件变量

        条件变量是一种用于线程同步的机制。它主要用于让线程在某个条件满足之前等待,当条件满足时,线程可以被唤醒并继续执行。在多线程编程环境中,条件变量提供了一种高效的方式来协调线程之间的执行顺序和资源访问。通常与互斥锁配合使用。互斥锁用于保护共享资源的访问,而条件变量用于线程的等待和唤醒操作。

        简单可以理解为他内部有一个标记位和一个线程等待队列,当一个线程申请到锁资源的时候,会访问临界区的代码,此时我们在临界区代码中判断条件变量是否就绪,如果说就绪的话,条件变量的标记位为1,就可以让申请到锁的进行继续访问临界区资源,如果说没有准备就绪的话,就把该线程链入到线程等待队列中,同时释放锁资源。当条件准备就绪之后,会唤醒等待队列的一个或多个进程继续竞争锁资源,竞争到锁资源的线程就可以访问临界区资源了。

        下面举一个场景,有两个线程和一个缓冲区临界资源,一个线程为写入线程,一个线程为读取线程。那么显而易见,需要先写入后读取,那么这就是线程之间访问临界资源的顺序,也就是线程的同步策略。那么如何实现呢?用一把锁和一个条件变量去实现,该条件变量关联的条件就是缓冲区中有数据。当读取进程申请到锁的时候,会进入条件变量的判断,因为条件变量没有就绪,所以会就读写进程链入到线程等待队列中,释放锁资源。那么写入线程就会竞争到锁资源,进行写入,写入之后,会调用条件变量就绪接口并唤醒等待该条件变量的等待队列中的线程,之后释放锁资源。那么读取线程就可以继续参与锁的竞争了,当获取到锁资源后,就可以读取临界区中的资源数据了。这就是使用条件变量和锁实现的线程同步。

3.条件变量接口

全局条件变量

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

条件变量创建

int pthread_cond_init(pthread_cont_t* cond, const pthread_condattr_t* attr);

条件变量销毁

int pthread_cond_destroy(pthread_cond_t* cond);

等待条件变量

int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex); 

唤醒等待队列中的线程

int pthread_cond_signal(pthread_cond_t* cond);           //唤醒队列头部的一个线程

int pthread_cond_broadcast(pthread_cond_t* cond);     //唤醒所有线程

接口的使用代码

#include <iostream>
#include <pthread.h>
#include <unistd.h>//全局锁
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//全局条件变量
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;void* handler(void* arg)
{while(true){//申请锁资源pthread_mutex_lock(&mutex);//等待条件变量就绪pthread_cond_wait(&cond, &mutex);//临界区std::cout << pthread_self() << "-thread entry critical section" << std::endl;//释放锁资源pthread_mutex_unlock(&mutex);}
}int main()
{//创建线程for(int i = 0; i < 5; i++){pthread_t tid;pthread_create(&tid, nullptr, handler, nullptr);//线程分离--无需等待pthread_detach(tid);}//反复让条件变量就绪,唤醒一个线程while(true){pthread_cond_signal(&cond);sleep(1);}return 0;
}

         对于在条件变量等待队列被唤醒的线程,也要重新参与锁的竞争,但是并非是跳到申请锁的那行代码,而是说该pthreat_cont_wait函数内部也是有申请锁的代码的。

相关文章:

[Linux]线程的互斥与同步

目录 一、互斥 1.互斥的概念 2.互斥锁接口 3.线程加锁解锁本质 4.死锁 二、同步 1.同步的概念 2.条件变量 3.条件变量接口 一、互斥 1.互斥的概念 互斥指的是任何时刻&#xff0c;互斥保证有且只有一个执行流进入临界区&#xff0c;进行临界资源的访问&#xff0c;通…...

Java:缓存:LinkedHashMap实现Lru

文章目录 Lru源码分析 ​​​​​​​​​​​​​​LinkedHashMap维护一个LinkedHashMapEntry<K,V>的双向链表对LinkedHashMap的增删查操作,也会对链表进行相同的操作并改变链表的链接顺序小结使用方法​​​​​​​​​​​​​​应用总结Lru Least Recently Used,…...

【形式篇】年终总结怎么写:PPT如何将内容更好地表现出来

——细节满满&#xff0c;看完立马写出一篇合格的PPT 总述 形式服务于内容&#xff0c;同时合理的形式可以更好地表达和彰显内容 年终总结作为汇报型PPT&#xff0c;内容一定是第一位的&#xff0c;在内容篇(可点击查看)已经很详细地给出了提纲思路&#xff0c;那如何落实到…...

自定义字典转换器用于easyExcel 导入导出

文章目录 引言I 字典转换器、注解、序列化器注解定义自定义字典转换器用于easyExcel 导入导出自定义字典序列化器II 字典存储设计数据库表结构redis缓存引言 需求导入Excel时,根据字典内容或者字段编码转换 导出Excel时,根据字典内容或者字段编码转换 接口响应数据序列化时,…...

0 Token 间间隔 100% GPU 利用率,百度百舸 AIAK 大模型推理引擎极限优化 TPS

1. 什么是大模型推理引擎 大模型推理引擎是生成式语言模型运转的发动机&#xff0c;是接受客户输入 prompt 和生成返回 response 的枢纽&#xff0c;也是拉起异构硬件&#xff0c;将物理电能转换为人类知识的变形金刚。 大模型推理引擎的基本工作模式可以概括为&#xff0c…...

js:事件流

事件流 事件流是指事件完整执行过程中的流动路径 一个事件流需要经过两个阶段&#xff1a;捕获阶段&#xff0c;冒泡阶段 捕获阶段是在dom树里获取目标元素的过程&#xff0c;从大到小 冒泡阶段是获取以后回到开始&#xff0c;从小到大&#xff0c;像冒泡一样 实际开发中大…...

Linux对比Windows

1. 性能和资源占用 Linux 更轻量级&#xff1a;Linux 内核设计简洁&#xff0c;占用系统资源&#xff08;如内存、CPU&#xff09;较少&#xff0c;适合高负载的服务器环境。 高效的多任务处理&#xff1a;Linux 在多任务处理和并发请求方面表现优异&#xff0c;适合处理大量并…...

Excel 技巧03 - 如何对齐小数位数? (★)如何去掉小数点?如何不四舍五入去掉小数点?

这几个有点儿关联&#xff0c;我都给放到一起了&#xff0c;不影响大家分别使用。 目录 1&#xff0c;如何对齐小数位数&#xff1f; 2&#xff0c;如何去掉小数点&#xff1f; 3&#xff0c;如何不四舍五入去掉小数点&#xff1f; 1&#xff0c;如何对齐小数位数&#xff…...

Vue3国际化多语言的切换

参考链接: link Vue3国际化多语言的切换 一、安装 vue-i18n 和 element-plus vue-i18n 是一个国际化插件&#xff0c;专为 Vue.js 应用程序设计&#xff0c;用于实现多语言支持。它允许你将应用程序的文本、格式和消息转换为用户的首选语言&#xff0c;从而提供本地化体验。…...

使用XAML语言仿写BiliBil登录界面

实现步骤 实现左右布局 使用了Grid两列的网格布局&#xff0c;第一列宽度占35%&#xff0c;第二列宽度占65%。使用容器布局Border包裹左右布局内容&#xff0c;设置背景色、设置圆角 <!-- 定义两列--> <Grid.ColumnDefinitions><ColumnDefinition Width &quo…...

机器学习和深度学习

机器学习&#xff08;Machine Learning&#xff0c;简称 ML&#xff09;和深度学习&#xff08;Deep Learning&#xff0c;简称 DL&#xff09;都是人工智能&#xff08;AI&#xff09;领域的重要技术&#xff0c;它们的目标是使计算机通过数据学习和自主改进&#xff0c;从而完…...

Word表格批量提取数据到Excel,Word导出到Excel,我爱excel

Word表格批量提取数据到Excel&#xff0c;Word导出到Excel - 我爱Excel助你高效办公 在日常办公中&#xff0c;Word表格常常用于记录和整理数据&#xff0c;但将这些数据从Word提取到Excel&#xff0c;特别是当涉及多个文件时&#xff0c;常常让人头疼。如果你经常需要将多个W…...

SpringSecurity抛出异常但AccessDeniedHandler不生效

文章目录 复现原因 复现 Beanpublic SecurityFilterChain securedFilterChain(HttpSecurity http) throws Exception {//...//异常http.exceptionHandling(except -> {except.authenticationEntryPoint(new SecurityAuthenticationEntryPoint());except.accessDeniedHandle…...

高清绘画素材3600多张动漫线稿线描上色练习参考插画原画

工作之余来欣赏一波线稿&#xff0c;不务正业版... 很多很多的线稿... 百度网盘 请输入提取码...

EXCEL技巧

1. EXCEL技巧 1.1. 截取表格内某个字符之前的所有字符 1.1.1.样例 在单元格内输入函数&#xff1a; # 截取A1单元格内“分”字符左边的所有字符 LEFT(A1,FIND("分",A1)-1)1.1.2.截图...

python制作翻译软件

本文复刻此教程&#xff1a;制作属于自己的翻译软件-很简单【Python】_哔哩哔哩_bilibili 一、明确需求&#xff08;以搜狗翻译为例&#xff09; &#xff08;1&#xff09;网址&#xff1a;https://fanyi.sogou.com/text &#xff08;2&#xff09; 数据&#xff1a;翻译内容…...

ollama+FastAPI部署后端大模型调用接口

ollamaFastAPI部署后端大模型调用接口 记录一下开源大模型的后端调用接口过程 一、ollama下载及运行 1. ollama安装 ollama是一个本地部署开源大模型的软件&#xff0c;可以运行llama、gemma、qwen等国内外开源大模型&#xff0c;也可以部署自己训练的大模型 ollama国内地…...

BERT:深度双向Transformer的预训练用于语言理解

摘要 我们介绍了一种新的语言表示模型&#xff0c;名为BERT&#xff0c;全称为来自Transformer的双向编码器表示。与最近的语言表示模型&#xff08;Peters等&#xff0c;2018a&#xff1b;Radford等&#xff0c;2018&#xff09;不同&#xff0c;BERT旨在通过在所有层中联合调…...

【AI-23】深度学习框架中的神经网络3

神经网络有多种不同的类型&#xff0c;每种类型都针对特定的任务和数据类型进行优化。根据任务的特点和所需的计算能力&#xff0c;可以选择适合的神经网络类型。以下是一些主要的神经网络类型及其适用的任务领域。 1. 深度神经网络&#xff08;DNN&#xff09; 结构&#xf…...

网站运营数据pv、uv、ip

想要彻底弄清楚pv uv ip的区别&#xff0c;首先要知道三者的定义&#xff1a; IP(独立IP)的定义&#xff1a; 即Internet Protocol,指独立IP数。24小时内相同公网IP地址只被计算一次。 PV(访问量)的定义&#xff1a; 即Page View,即页面浏览量或点击量&#xff0c;用户每次刷…...

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…...

大型活动交通拥堵治理的视觉算法应用

大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动&#xff08;如演唱会、马拉松赛事、高考中考等&#xff09;期间&#xff0c;城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例&#xff0c;暖城商圈曾因观众集中离场导致周边…...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例

文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命

在华东塑料包装行业面临限塑令深度调整的背景下&#xff0c;江苏艾立泰以一场跨国资源接力的创新实践&#xff0c;重新定义了绿色供应链的边界。 跨国回收网络&#xff1a;废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点&#xff0c;将海外废弃包装箱通过标准…...

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

C# SqlSugar:依赖注入与仓储模式实践

C# SqlSugar&#xff1a;依赖注入与仓储模式实践 在 C# 的应用开发中&#xff0c;数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护&#xff0c;许多开发者会选择成熟的 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;SqlSugar 就是其中备受…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

如何在最短时间内提升打ctf(web)的水平?

刚刚刷完2遍 bugku 的 web 题&#xff0c;前来答题。 每个人对刷题理解是不同&#xff0c;有的人是看了writeup就等于刷了&#xff0c;有的人是收藏了writeup就等于刷了&#xff0c;有的人是跟着writeup做了一遍就等于刷了&#xff0c;还有的人是独立思考做了一遍就等于刷了。…...

代理篇12|深入理解 Vite中的Proxy接口代理配置

在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...