Linux初阶——线程(Part2):互斥同步问题

news2024/11/8 10:41:11

一、互斥锁

1、CPU 运算过程

执行完整个语句后,才会把数据写入内存;如果执行时被中断,那么数据和上下文就会保存到线程的 TCB,但数据并不会被写入内存。

1.1. 当 CPU 执行完整个语句时

CPU 最终执行完整个语句的过程

就用上图举个例子。 CPU 是如何执行 a-- 这个句代码的呢?

  • 第一步,CPU 将内存中变量 a 的值(100)拷贝到运算器中。
  • 第二步,在运算器中减一。
  • 第三步,把新的值(99)拷回变量 a 里。

1.2. 当 CPU 还未执行完这个语句,线程就被换出时

CPU 执行完减 1 操作,线程就被换出了

以上图为例,CPU 刚执行完减 1 操作,线程就被换出。遇到这种没执行完语句的情况,CPU 会把计算数据和该线程执行的上下文(即该线程执行到哪一行代码)拷到该线程的 TCB 里;然后再为下一个线程服务。

2、解决方案——互斥锁(mutex)

2.0. 什么是互斥问题

当多个线程并发执行,并访问同一个变量时,就会很容易发生变量的一致性问题。

如图所示,假如我想让变量 a 从 100 减到 0.

线程 1 在做完 a-- 操作后就被换出了:
 

线程 1 被换出

线程 2 正常执行完整个减 1 操作,并重复了很多次,直到减到 10,被换出,线程 1 进来了:

线程2被换出
线程 1 执行

而此时 a 这个全局变量又从 10 变成 100 了。

所以,这个变量从始至终都只能被一个执行流(线程)访问。

2.1. 互斥锁原理

下面这是线程申请锁和释放锁的汇编代码:

lock: // 申请锁
    movb $0, %al
    xchgb %al, mutex
    if (al 寄存器的内容 > 0) return 0; // 申请锁成功
    else 挂起等待;
    goto lock;

unlock: // 释放锁
    movb $1, mutex
    唤醒等待 Mutex 的线程;
    return 0;
lock 部分的汇编代码的前 2 行的执行过程

但是,如果当该线程执行完第一句后就被换出了,那么该线程会把 al 寄存器的值和上下文保存到 TCB;然后再被换出;然后第二个线程再进来从上次的上下文开始接着执行。所以,其实申请锁的本质就是看哪个线程拿到 1,拿到 1 的那个线程就申请到锁了。

2.2. 相关函数

2.2.1. pthread_mutex_t 类型

就是互斥锁的类型。

2.2.2. pthread_mutex_lock 函数

这个函数用于申请互斥锁。注意,这里只是把锁加上而已,并不会创建锁。 

2.2.3. pthread_mutex_unlock 函数

这个函数用于释放锁。注意,这里只是把锁解开了,锁还在。

2.2.4. 初始化全局的锁
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
2.2.5. 初始化局部变量的锁 
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);

参数:
mutex:要初始化的互斥锁
attr:nullptr
 2.2.6. 销毁互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);

这个函数是直接让锁消失。

注意:

  • 如果创建的锁是用宏创建的,就不能掉这个函数。 
  • 一定要在解开锁了之后才能调用这个函数。
  • 确保后面没有线程用到这个锁。

2.3. 临界区

处于 pthread_mutex_lock 函数和 pthread_mutex_unlock 函数之间的区域就是临界区。

2.4. 临界资源

多线程执行流共享的资源就叫做临界资源。不过,临界资源都是每次只能让一个一个线程访问的。但是当线程申请锁时,那么锁也是共享资源;所以申请锁和释放锁本来就被汇编语言设计成原子性的(即只用一句汇编实现)。

二、死锁

1、产生死锁的必要条件

  • 互斥条件:一个资源每次只能被一个执行流使用。
  • 请求和保持条件:一个执行流因请求资源而阻塞时,它同时也持有已有的资源,且不释放。
  • 不剥夺条件:如果申请不到资源,只能等待,不能强行抢占。
  • 循环条件:执行流之间形成有环的等待资源的关系。

2、如何解决

  • 破坏上面的 4 条必要条件的其中一条。
  • 资源一次性分配。
  • 加锁顺序一致。
  • 避免未释放的场景。

三、同步问题

1、什么是同步

同步就是让线程按照一定顺序获取资源,而不是一窝蜂地去抢。举个例子,假如厕所是一个共享资源,一次只能一个人用。当有人进去后,就会有很多人在外面等。如果不同步的话,一旦厕所里的那个人出来,全部人就会一窝蜂地去抢厕所;而如果刚出来的那个人的抢占能力非常强,那么厕所就会一直被抢占能力最强的那个人占用,因为这个人可以出来后再抢。于是就会导致其他人长时间没得上厕所,进而引发其他人的饥饿问题。

因此,解决方法就是让所有人排队,一个个上厕所。从厕所出来的人无论抢占能力强还是弱,都要去队尾排队。这种让人们有序地使用厕所就是同步。而把线程看成人,那就就是线程的同步了。所以,总结来说,在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题,叫做同步。

2、条件变量

2.1. 什么是条件变量

还记得前面厕所排队的例子吗?条件变量就是这个等待队列的头节点,当申请到资源时,线程就会从等待队列出来,然后访问共享资源;而如果线程没申请到资源时,该线程就会进入以条件变量为头节点的等待队列里。

2.2. 相关函数

2.2.0. pthread_cond_t 类型

该类型为条件变量类型。

2.2.1. 初始化全局的条件变量
pthread_cond_t global_cond = PTHREAD_COND_INITIALIZER;
2.2.1. 初始化局部变量的条件变量

参数介绍

  • cond:条件变量的地址。
  • attr: 条件变量的性质。

这个函数用于初始化条件变量。其实就是初始化等待队列的头节点。

2.2.2. pthread_cond_destroy 函数

参数介绍

  • cond:传条件变量的地址。 

注意:

  • 如果创建的条件变量是用宏创建的,就不能掉这个函数。 

这个函数用于释放条件变量。其实就是初始化等待队列的头节点。

2.2.3. pthread_cond_wait 函数

参数介绍

cond:传条件变量的地址。

mutex:传互斥锁的地址。 

此函数用于把线程加入等待队列。如果此时没申请到共享资源,该线程就会进入以条件变量为头节点的等待队列里。

2.2.4. pthread_cond_signal 函数

参数介绍

  • cond:条件变量地址。 

此函数用于唤醒线程。 即当线程申请到共享资源时,这个函数就能让一个线程的 TCB 退出以条件变量为头节点的等待队列。

2.2.5. pthread_cond_broadcast 函数

参数介绍

  • cond:条件变量地址。 

即当线程申请到共享资源时,这个函数就能让所有线程的 TCB 退出以条件变量为头节点的等待队列。 

四、应用:生产者消费者问题(cp 问题)

1、什么是生产者消费者问题

简单来说,前提就是生产者和消费者之间有一个共享资源(共享内存),生产者负责向共享内存放数据,消费者负责从共享内存里拿数据。然后我们通过一定的策略解决它们的互斥与同步问题。

2、3 种关系

2.1. 生产者与生产者

生产者与生产者之间是竞争关系,因此生产者与生产者之间是互斥关系。

2.2. 生产者与消费者

2.2.1. 互斥关系

因为要保证公共资源的安全性,因此要让生产者与消费者处于互斥关系,每次只能有一个线程访问共享资源。

2.2.2. 同步关系

因为共享内存里先有数据,消费者才可以拿数据。而生产者是负责向内存放数据的。因此先有生产者向共享内存放数据,才有消费者从共享内存拿数据,这是一种顺序访问共享内存;因此生产者与消费者也处于同步关系。

2.3. 消费者与消费者

消费者与消费者之间是竞争关系,因此生产者与生产者之间是互斥关系。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux学习_10

第九章Linux文件系统权限 主要包括:文件的一般权限,特殊权限,ACL权限,权限掩码umask 文件的一般权限 文件详细信息 文件权限构成 权限针对三类对象定义 owner:所有者,缩写u group :所属组&#…

Easysearch 与 LLM 融合打造知识库系统

文章目录 一、LangChain 简介二、RAG 产生的背景及其局限性三、RAG 工作流程四、 Easysearch 结合 LLM 实现 RAG(1)Easysearch 简介(2)结合实现RAG 五、 Easysearch 结合 LLM 实现 RAG 的优势(1)提高检索准…

【SSM详细教程】-16-SSM整合超详细讲解

精品专题: 01.《C语言从不挂科到高绩点》课程详细笔记 https://blog.csdn.net/yueyehuguang/category_12753294.html?spm1001.2014.3001.5482 02. 《SpringBoot详细教程》课程详细笔记 https://blog.csdn.net/yueyehuguang/category_12789841.html?spm1001.20…

一个图像处理的实验设计

在Rafael Gonzalez和Richard Woods的《数字图像处理》中有一道这样的实验设计题,我发现特别适合说明多个阈值的全局阈值分割的示例。 我嫌他说话啰嗦,修改了一下作为考试题。 基本流程 图像分割 选取中间灰度级的区域标记。 2. 形态学后处理 开运…

如何在Linux系统中使用Nginx部署静态网站

如何在Linux系统中使用Nginx部署静态网站 Nginx简介 安装Nginx 在Debian/Ubuntu系统中安装 在CentOS/RHEL系统中安装 启动Nginx服务 验证Nginx是否正在运行 配置Nginx服务器块 编辑服务器块配置 示例服务器块配置 创建网站根目录 准备静态网站内容 创建示例HTML页面 测试Ngi…

Mysql报错注入之floor报错详解

updatexml extractvalue floor 是mysql的函数 groupbyrandfloorcount 一、简述 利用 select count(),(floor(rand(0)2))x from table group by x,导致数据库报错,通过 concat 函数,连接注入语句与 floor(rand(0)*2)函数,实现将…

Redis 命令集 (超级详细)

目录 Redis 常用命令集 string类型 hash类型 list类型 set类型 zset类型 bitmap 类型 geo 类型 GEOADD (添加地理位置的坐标) GEOPOS (获取地理位置的坐标) GEODIST (计算两个位置之间的距离) GEOHASH (返回一个或多个位置对象的 geohash 值) GEORADIUS (根据用户…

nuxt数据库之增删改查,父组件子组件传值

nuxt学到数据库这里,就涉及到响应数据,父组件向子组件传值,子组件向父组件传值,最终还是需要掌握vue3的组件知识了。学习真的是一个长期的过程,不管学习了什么知识,有多少,都应该及时的记录下来…

如何在Linux系统中使用Zabbix进行监控

如何在Linux系统中使用Zabbix进行监控 Zabbix简介 安装Zabbix 在Debian/Ubuntu系统中安装 在CentOS/RHEL系统中安装 配置Zabbix数据库 创建数据库 导入数据库 配置Zabbix服务器 访问Zabbix Web界面 完成初始配置 配置Zabbix Agent 安装Agent 配置Agent 添加主机到Zabbix 创…

《Counterfeiting Scalable Detection Image Based System for E-commerce》中文校对版

文章汉化系列目录 文章目录 文章汉化系列目录摘要CCS 概念:关键词1 引言2 E-CoS2.1 e-CoS架构2.2 e-CoS 模块2.2.1 图像相似度模块2.2.2 索引和搜索模块2.2.3 索引2.2.4 搜索 3 性能考量3.1 示例 13.2 示例 23.3 示例 3 4 结论致谢 摘要 电子商务中的假冒问题通过本…

前端开发设计模式——观察者模式

目录 一、定义和特点 1. 定义 2. 特点 二、实现方式 1. 使用 JavaScript 实现观察者模式的基本结构 2. 实际应用中的实现示例 三、使用场景 1. 事件处理 2. 数据绑定 3. 异步通信 4. 组件通信 四、优点 1. 解耦和灵活性 2. 实时响应和数据一致性 3. 提高代码的可…

思科--交换网络综合实验

前言 之前一直在学华为ENSP的命令,最近来了个实验(被坑了),要求是用思科完成。没法子,就弄呗 拓扑图 实验目标 首先配置以太通道(逻辑上的)实现链路冗余和负载共享 在交换机接口配置trunk&#…

推荐一款开源的免费PDF编辑工具:CubePDF Utility

CubePDF Utility是一款功能强大的开源免费PDF编辑器,它采用了基于缩略图的界面设计,为用户提供了直观且高效的PDF编辑体验。该软件特别针对那些希望以简单直观方式编辑 PDF 文件的用户而设计,支持多种操作,如合并、提取、拆分、更…

探索C嘎嘎:初步接触STL

#1024程序员节|征文# 前言: 在前文小编讲述了模版初阶,其实讲述模版就是为了给讲STL提前铺垫,STL是C中很重要的一部分,各位读者朋友要知道它的份量,下面废话不多说,开始走进STL的世界。 目录&am…

【java】java的基本程序设计结构03-charboolean

char类型 代表 字符--符号---几何图形 大小由编码类型决定。 char是基本类型,但String不是。 String是Java中的一个类,属于引用类型; char中只能放一个字符。 char a‘a’; //任意单个字符,加单引号。 char a‘中’;//任意单个中文…

22-Carla AD 代理

CARLA AD agent是一种AD agent,它可以遵循给定的路线,避免与其他车辆相撞,并通过访问地面真实数据来遵守红绿灯的状态。CARLA AD demo使用它来提供一个如何使用ROS桥接的示例。 在内部,CARLA AD Agent使用单独的节点进行局部规划。…

Could not find the planner configuration ‘None‘ on the param server

moveit中运行demo.launch报错:Could not find the planner configuration ‘None’ on the param server 打开config文件夹下的config,找到ompl_planning.yaml文件,找到: arm: default_planner_config: None gripper: default_p…

微信支付宝小程序SEO优化的四大策略

在竞争激烈的小程序市场中,高搜索排名意味着更多的曝光机会和潜在用户。SEO即搜索引擎优化,对于小程序而言,主要指的是在微信小程序商店中提高搜索排名,从而增加曝光度和用户访问量。有助于小程序脱颖而出,提升品牌知名…

Servlet 3.0 注解开发

文章目录 Servlet3.0注解开发修改idea创建注解的servlet模板内容讲解 关于servlet3.0注解开发的疑问_配置路径省略了属性urlPatterns内容讲解内容小结 Servlet3.0注解开发 【1】问题 说明:之前我们都是使用web.xml进行servlet映射路径的配置。这样配置的弊端&…

如何把网页的图片批量下载?3批量下载方法有详细步骤

如何把网页的图片批量下载?在浏览网页时,我们经常会遇到内容丰富、图片繁多的页面,无论是欣赏美图集、研究设计案例,还是收集教学素材,这些图片往往都是宝贵的资源。然而,一张张手动保存不仅耗时费力&#…