Linux线程(三)死锁与线程同步
目录
一、什么是死锁
死锁的四个必要条件
如何避免死锁
避免死锁算法
二、Linux线程同步
三 、条件变量
1、条件变量基本原理
2、条件变量的使用
3、条件变量使用示例
为什么 pthread_cond_wait 需要互斥量?
一、什么是死锁
死锁是计算机科学中的一个概念,特别是在操作系统和多线程编程领域中经常遇到。它指的是两个或两个以上的进程或线程在执行过程中,由于互相等待对方持有的资源而无法继续执行的状态。具体来说,每个进程都已经占有了某些资源,但还需要额外的、目前被其他进程所占有的资源才能继续执行。这样,所有涉及的进程都进入了等待状态,形成了一个相互依赖的循环,如果没有外部干预,它们将永远等待下去,无法自行解除阻塞状态。
死锁的四个必要条件
互斥条件:一个资源每次只能被一个执行流使用请求与保持条件:一个执行流因请求资源而阻塞时,对已获得的资源保持不放不剥夺条件:一个执行流已获得的资源,在末使用完之前,不能强行剥夺循环等待条件:若干执行流之间形成一种头尾相接的循环等待资源的关系
如何避免死锁
破坏死锁的四个必要条件(破坏其中之一即可)加锁顺序一致避免锁未释放的场景资源一次性分配
避免死锁算法
死锁检测算法 是一种动态检测系统中是否已经发生死锁的方法。它不需要事先采取措施去预防死锁,而是允许系统运行,并定期检查是否有死锁的存在。基本思想是构造一个资源分配图(或称作前驱图),在这个图中,节点代表进程和资源,边表示分配关系和请求关系。如果图中存在环路,则表示系统处于死锁状态。具体步骤包括:
构建资源分配图:图中的节点分为两类,一类代表进程,另一类代表资源类型。从每个进程节点出发的边指向它已分配的资源节点,从每个资源节点出发的边指向请求该资源的进程节点。
检测环路:使用拓扑排序或深度优先搜索等算法检测图中是否存在环。如果存在环,则说明有进程等待的资源被其他在环中的进程所占有,形成死锁。
处理死锁:一旦检测到死锁,系统可以选择采取不同的策略来解决,比如终止某些进程、回滚进程状态或强制释放资源等。
银行家算法 是一种避免死锁的策略,而不是检测死锁。它通过预判分配资源的行为是否安全来避免系统进入不安全状态,从而防止死锁发生。算法核心包括以下几个步骤:
初始化:记录系统中所有可用资源的数量以及每个进程对各类资源的最大需求、已分配资源和当前还需要的资源。
安全性检查:算法在每次分配资源之前,会先检查这次分配是否会导致系统进入不安全状态。这通过试探性地分配资源,然后检查是否存在一个安全序列,即所有进程能够按照某种顺序完成执行,而不会发生某个进程因为缺少资源而无法继续的情况。
资源分配:只有当试探性分配后系统仍处于安全状态时,才会真正分配资源给请求的进程。
资源回收:当进程完成任务后,必须归还所有分配给它的资源,以便其他进程可以使用。
银行家算法的核心在于其预防机制,确保了即使在资源有限的情况下,系统也能保证进程按照某种顺序安全地执行完毕,避免了死锁的发生。
二、Linux线程同步
在Linux环境下,条件变量是线程同步的一种机制,用于实现线程间的协作,使得一个线程能够等待某个条件变为真,而另一个线程负责改变这个条件并通知等待的线程。条件变量通常与互斥锁一起使用,以确保在检查条件和修改条件时的原子性和一致性。
同步(Synchronization)是指在多线程或多进程环境中,协调不同执行单元的操作顺序,确保它们按照预定的方式执行,以避免数据不一致或逻辑错误的问题。同步机制确保了对共享资源的访问是有序的,避免了竞态条件的出现。
竞态条件(Race Condition)是指在多线程程序中,多个线程对同一块数据进行非同步的访问和修改,其最终结果取决于线程的调度顺序。由于线程执行的交错,可能会导致数据不一致、计算错误或者程序行为不符合预期。
三 、条件变量
1、条件变量基本原理
等待条件:当一个线程发现某个条件不满足时,它可以调用pthread_cond_wait()函数,这会自动释放它之前锁定的互斥锁,并使线程进入等待状态,直到其他线程通过信号机制唤醒它。此时,线程会重新尝试获取互斥锁,并检查条件是否满足,如果不满足则可能再次进入等待状态。
发送信号:当另一个线程改变了条件变量相关的状态,并希望唤醒等待的线程时,它会调用pthread_cond_signal()或pthread_cond_broadcast()函数。pthread_cond_signal()会唤醒一个等待该条件变量的线程(如果有多个线程在等待,则选择其中一个),而pthread_cond_broadcast()会唤醒所有等待该条件的线程。
2、条件变量的使用
初始化条件变量:可以通过静态初始化或者动态初始化来创建条件变量。
静态初始化使用PTHREAD_COND_INITIALIZER宏;
动态初始化则使用pthread_cond_init()函数。
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict
attr);
参数:
cond:要初始化的条件变量
attr:NULL
锁定互斥锁:在检查或修改条件之前,线程需要先获取互斥锁,以确保操作的原子性和互斥性。
检查条件:线程检查条件是否满足,如果不满足则调用pthread_cond_wait()进入等待状态。
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
参数:
cond:要在这个条件变量上等待
mutex:互斥量,后面详细解释
改变条件:在另一个线程中,当条件改变后,应先锁定相同的互斥锁,改变条件,然后调用pthread_cond_signal()或pthread_cond_broadcast()来唤醒等待线程。
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
解锁互斥锁:无论是在调用pthread_cond_wait()前后,还是在改变条件之后,都需要正确地解锁互斥锁。
清理:不再使用条件变量时,动态初始化的条件变量需要通过pthread_cond_destroy()函数进行清理。
int pthread_cond_destroy(pthread_cond_t *cond)
3、条件变量使用示例
使用条件变量(pthread_cond_t)和互斥锁(pthread_mutex_t)的经典示例,实现了线程间的简单同步。使得线程2与线程1交替打印。
代码示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
pthread_cond_t cond;
pthread_mutex_t mutex;
void *r1(void *arg)
{while (1){pthread_cond_wait(&cond,&mutex);printf("我是线程1\n");}
}
void *r2(void *arg)
{while (1){printf("我是线程2\n");pthread_cond_signal(&cond);sleep(1);}
}
int main(void)
{pthread_t t1, t2;pthread_cond_init(&cond, NULL);pthread_mutex_init(&mutex, NULL);pthread_create(&t1, NULL, r1, NULL);pthread_create(&t2, NULL, r2, NULL);pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);
}
r1函数作为线程1的入口点,它在一个无限循环中调用pthread_cond_wait(&cond, &mutex)。这意味着线程1会释放互斥锁mutex并阻塞,直到其他线程通过pthread_cond_signal或pthread_cond_broadcast唤醒它。一旦被唤醒,它会重新获取互斥锁并打印消息“我是线程1”。
r2函数作为线程2的入口点,在其循环中打印“我是线程2”,随后调用pthread_cond_signal(&cond)来唤醒一个等待在cond上的线程(在这种情况下,通常是线程1)。之后,sleep(1)让线程2暂停1秒,模拟工作与同步的间隔。
运行结果:

为什么 pthread_cond_wait 需要互斥量?
条件等待是线程间同步的一种手段,如果只有一个线程,条件不满足,一直等下去都不会满足,所以必须要有一个线程通过某些操作,改变共享变量,使原先不满足的条件变得满足,并且友好的通知等待在条件变量上的线程。条件不会无缘无故的突然变得满足了,必然会牵扯到共享数据的变化。所以一定要用互斥锁来保护。没有互斥锁就无法安全的获取和修改共享数据。
如果先上锁,发现条件不满足,解锁,然后等待在条件变量可以吗?
// 错误的设计
pthread_mutex_lock(&mutex);
while (condition_is_false) {
pthread_mutex_unlock(&mutex);
//解锁之后,等待之前,条件可能已经满足,信号已经发出,但是该信号可能被错过
pthread_cond_wait(&cond);
pthread_mutex_lock(&mutex);
}
pthread_mutex_unlock(&mutex);
如果在
pthread_mutex_unlock(&mutex);和pthread_cond_wait(&cond);之间,其他线程改变了条件并调用了pthread_cond_signal或pthread_cond_broadcast,那么这个信号可能会被错过。因为pthread_cond_wait实际上是在调用时才检查是否应该唤醒线程,而这时线程可能已经错过了信号。竞态条件:在解锁互斥锁后检查条件,然后等待,这期间其他线程可能又修改了条件状态,导致线程可能在不应该等待的情况下进入等待状态,或者即使条件已经满足仍然进入等待。
由于解锁和等待不是原子操作。调用解锁之后, pthread_cond_wait 之前,如果已经有其他线程获取到互斥量,摒弃条件满足,发送了信号,那么 pthread_cond_wait 将错过这个信号,可能会导致线程永远阻塞在这个 pthread_cond_wait 。所以解锁和等待必须是一个原子操作。
正确的做法是将条件检查放在pthread_cond_wait内部,确保在检查条件和等待之间不会错过任何信号。
等待条件代码:
pthread_mutex_lock(&mutex);
while (condition_is_false) { // 条件检查放在循环内pthread_cond_wait(&cond, &mutex); // 等待时保持互斥锁锁定
}
// 当条件满足时,会从pthread_cond_wait返回
pthread_mutex_unlock(&mutex);
pthread_mutex_lock(&mutex);
设置条件为真
pthread_cond_signal(cond);
pthread_mutex_unlock(&mutex); 这样的设计确保了当线程准备等待时,如果条件已经满足(可能是由于其他线程的操作),它不会错过这一事实,并且可以直接继续执行,避免了信号丢失和不必要的等待。
条件变量和互斥锁的正确配合使用对于避免死锁、竞态条件和信号丢失至关重要。始终遵循“在持有锁的情况下检查条件,然后等待”的原则,确保线程安全和高效的同步。
相关文章:
Linux线程(三)死锁与线程同步
目录 一、什么是死锁 死锁的四个必要条件 如何避免死锁 避免死锁算法 二、Linux线程同步 三 、条件变量 1、条件变量基本原理 2、条件变量的使用 3、条件变量使用示例 为什么 pthread_cond_wait 需要互斥量? 一、什么是死锁 死锁是计算机科学中的一个概念,…...
SpringAMQP 发布订阅-TopicExchange
根据这个模型编写代码: RabbitListener(bindings QueueBinding(value Queue(name "topic.queue1"),exchange Exchange(name "itcast.topic",type ExchangeTypes.TOPIC),key {"china.#"}))public void listenTopicQueue1(String msg){Syst…...
uniapp h5 配置代理服务器
"devServer": {"disableHostCheck": true,"proxy": {"/api": {// 需要被代理的后台地址"target": "http://自己的地址","changeOrigin": true,"secure": false,"pathRewrite": {&q…...
使用Apache Spark从MySQL到Kafka再到HDFS的数据转移
使用Apache Spark从MySQL到Kafka再到HDFS的数据转移 在本文中,将介绍如何构建一个实时数据pipeline,从MySQL数据库读取数据,通过Kafka传输数据,最终将数据存储到HDFS中。我们将使用Apache Spark的结构化流处理和流处理功能&#…...
一篇文章拿下Redis 通用命令
文章目录 Redis数据结构介绍Redis 通用命令命令演示KEYSDELEXISTSEXPIRE RedisTemplate 中的通用命令 本篇文章介绍 Redis 的通用命令, 通用命令在 Redis 的所有数据类型下都使用, 学好通用命令可以让我们更好的使用 Redis. Redis数据结构介绍 Redis 是一个key-value的数据库&…...
锂电池充电充放电曲线分析
前言 锂电池的充电曲线通常包括三个阶段:恒流充电阶段、恒压充电阶段和滞后充电阶段。在恒流充电阶段,电流保持恒定,电压逐渐增加;在恒压充电阶段,电压保持恒定,电流逐渐减小;在滞后充电阶段,电流进一步减小,电池开始充满。通过监测这些阶段的电流和电压变化,可以评…...
vue3 第二十九节 (vue3 事件循环之nextTick)
引言 vue 项目中为什么要使用 nextTick 这个函数,是做什么用的,解决了哪些问题 1、nextTick 作用 用于处理DOM更新完成之后,执行回调函数的方法; 2、实现方案 vue2 中 nextTick() 是基于浏览器的 异步队列和微任务队列而执行…...
使用Flask-SocketIO构建实时Web应用
文章目录 准备工作编写代码编写HTML模板运行应用 随着互联网的发展,实时性成为了许多Web应用的重要需求之一。传统的HTTP协议虽然可以实现实时通信,但是其长轮询等机制效率低下,无法满足高并发、低延迟的需求。为了解决这一问题,诞…...
可重构柔性装配产线:为工业制造领域注入了新的活力
随着科技的飞速发展,智能制造正逐渐成为引领工业革新的重要力量。在这一浪潮中,可重构柔性装配产线以其独特的技术优势和创新理念,为工业制造领域注入了新的活力,开启了创新驱动的智能制造新篇章。 可重构柔性装配产线是基于富唯智…...
懒人网址导航源码v3.9
测试环境 宝塔Nginx -Tengine2.2.3的PHP5.6 MySQL5.6.44 为防止调试错误,建议使用测试环境运行的php与mysql版本 首先用phpMyAdmin导入数据库文件db/db.sql 如果导入不行,请直接复制数据库内容运行sql语句也可以 再修改config.php来进行数据库配置…...
springboot 开启缓存 @EnableCaching(使用redis)
添加依赖 pom.xml <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency>application.yml 配置redis连参数 spring:# redis 配置redis:# 地址host: 127.0.0.…...
Adobe After Effects AE v24.3.0 解锁版 (视频合成及视频特效制作)
Adobe系列软件安装目录 一、Adobe Photoshop PS 25.6.0 解锁版 (最流行的图像设计软件) 二、Adobe Media Encoder ME v24.3.0 解锁版 (视频和音频编码渲染工具) 三、Adobe Premiere Pro v24.3.0 解锁版 (领先的视频编辑软件) 四、Adobe After Effects AE v24.3.0 解锁版 (视…...
Qt---文件系统
一、基本文件操作 1. QFile对文件进行读和写 QFile file( path 文件路径) 读: file.open(打开方式) QlODevice::readOnly 全部读取->file.readAll(),按行读->file.readLine(),atend()->判断是否读到文件尾 …...
ruoyi-vue-pro 使用记录(2)
ruoyi-vue-pro 使用记录(2) 数据库商城商品模块数据表营销数据库交易数据库统计数据库 数据库 商城 参考官方文档 ruoyi-vue-pro yudao 项目商城 mall 模块启用及相关SQL脚本 商品模块(中心)以 product_ 作为前缀的表交易模块…...
centos7中如何全局搜索一下nginx的配置文件?
在CentOS 7中搜索Nginx的配置文件,你可以使用一些常用的命令行工具,比如find、grep等。这些工具可以帮助你在文件系统中查找文件,也可以用来查找Docker容器内部的文件,只要你知道如何访问容器的文件系统。 1. 搜索系统中的Nginx配…...
2024年5月10日有感复盘
2024年5月10日有感复盘 时间 今天是一个很美好的一天,原因是很平凡,读书很平凡,玩游戏很平凡,然后生活很平凡,未来可期,听歌很舒服,很喜欢一个人呆在图书馆的感觉,很喜欢发呆&…...
C++通过json文件配置参数
一、安装nlohmann json nlohmann json:安装_nlohmann安装-CSDN博客 依次执行下面指令: git clone https://gitee.com/cuihongxi/mov_from_github.gitcd json-developmkdir buildcd buildcmake ..makesudo make install 二、安装完成后使用 #include…...
idea连接远程仓库
git ->克隆。 url为远程仓库的地址,输入好后,选择项目存放目录,再点击克隆 点击新窗口打开。 切换到对应分支...
初始Django
初始Django 一、Django的历史 Django 是从真实世界的应用中成长起来的,它是由堪萨斯(Kansas)州 Lawrence 城中的一个网络开发小组编写的。它诞生于 2003 年秋天,那时 Lawrence Journal-World 报纸的程序员 Adrian Holovaty 和…...
leetcode56--合并区间
题目描述 以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。 示例 1: 输入:interv…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...
如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问(基础概念问题) 1. 请解释Spring框架的核心容器是什么?它在Spring中起到什么作用? Spring框架的核心容器是IoC容器&#…...
