[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.互斥的概念 互斥指的是任何时刻,互斥保证有且只有一个执行流进入临界区,进行临界资源的访问,通…...
Java:缓存:LinkedHashMap实现Lru
文章目录 Lru源码分析 LinkedHashMap维护一个LinkedHashMapEntry<K,V>的双向链表对LinkedHashMap的增删查操作,也会对链表进行相同的操作并改变链表的链接顺序小结使用方法应用总结Lru Least Recently Used,…...

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

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

0 Token 间间隔 100% GPU 利用率,百度百舸 AIAK 大模型推理引擎极限优化 TPS
1. 什么是大模型推理引擎 大模型推理引擎是生成式语言模型运转的发动机,是接受客户输入 prompt 和生成返回 response 的枢纽,也是拉起异构硬件,将物理电能转换为人类知识的变形金刚。 大模型推理引擎的基本工作模式可以概括为,…...

js:事件流
事件流 事件流是指事件完整执行过程中的流动路径 一个事件流需要经过两个阶段:捕获阶段,冒泡阶段 捕获阶段是在dom树里获取目标元素的过程,从大到小 冒泡阶段是获取以后回到开始,从小到大,像冒泡一样 实际开发中大…...
Linux对比Windows
1. 性能和资源占用 Linux 更轻量级:Linux 内核设计简洁,占用系统资源(如内存、CPU)较少,适合高负载的服务器环境。 高效的多任务处理:Linux 在多任务处理和并发请求方面表现优异,适合处理大量并…...

Excel 技巧03 - 如何对齐小数位数? (★)如何去掉小数点?如何不四舍五入去掉小数点?
这几个有点儿关联,我都给放到一起了,不影响大家分别使用。 目录 1,如何对齐小数位数? 2,如何去掉小数点? 3,如何不四舍五入去掉小数点? 1,如何对齐小数位数ÿ…...

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

使用XAML语言仿写BiliBil登录界面
实现步骤 实现左右布局 使用了Grid两列的网格布局,第一列宽度占35%,第二列宽度占65%。使用容器布局Border包裹左右布局内容,设置背景色、设置圆角 <!-- 定义两列--> <Grid.ColumnDefinitions><ColumnDefinition Width &quo…...
机器学习和深度学习
机器学习(Machine Learning,简称 ML)和深度学习(Deep Learning,简称 DL)都是人工智能(AI)领域的重要技术,它们的目标是使计算机通过数据学习和自主改进,从而完…...
Word表格批量提取数据到Excel,Word导出到Excel,我爱excel
Word表格批量提取数据到Excel,Word导出到Excel - 我爱Excel助你高效办公 在日常办公中,Word表格常常用于记录和整理数据,但将这些数据从Word提取到Excel,特别是当涉及多个文件时,常常让人头疼。如果你经常需要将多个W…...
SpringSecurity抛出异常但AccessDeniedHandler不生效
文章目录 复现原因 复现 Beanpublic SecurityFilterChain securedFilterChain(HttpSecurity http) throws Exception {//...//异常http.exceptionHandling(except -> {except.authenticationEntryPoint(new SecurityAuthenticationEntryPoint());except.accessDeniedHandle…...

高清绘画素材3600多张动漫线稿线描上色练习参考插画原画
工作之余来欣赏一波线稿,不务正业版... 很多很多的线稿... 百度网盘 请输入提取码...

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

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

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

BERT:深度双向Transformer的预训练用于语言理解
摘要 我们介绍了一种新的语言表示模型,名为BERT,全称为来自Transformer的双向编码器表示。与最近的语言表示模型(Peters等,2018a;Radford等,2018)不同,BERT旨在通过在所有层中联合调…...
【AI-23】深度学习框架中的神经网络3
神经网络有多种不同的类型,每种类型都针对特定的任务和数据类型进行优化。根据任务的特点和所需的计算能力,可以选择适合的神经网络类型。以下是一些主要的神经网络类型及其适用的任务领域。 1. 深度神经网络(DNN) 结构…...
网站运营数据pv、uv、ip
想要彻底弄清楚pv uv ip的区别,首先要知道三者的定义: IP(独立IP)的定义: 即Internet Protocol,指独立IP数。24小时内相同公网IP地址只被计算一次。 PV(访问量)的定义: 即Page View,即页面浏览量或点击量,用户每次刷…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...

vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...

基于IDIG-GAN的小样本电机轴承故障诊断
目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) 梯度归一化(Gradient Normalization) (2) 判别器梯度间隙正则化(Discriminator Gradient Gap Regularization) (3) 自注意力机制(Self-Attention) 3. 完整损失函数 二…...
多模态图像修复系统:基于深度学习的图片修复实现
多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...

day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分: 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...