Linux线程同步(2)死锁与互斥锁

死锁(Deadlock)是指两个或两个以上的进程(或线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程(或线程)称为死锁进程(或线程)。

死锁的产生需要满足四个必要条件,这四个条件被称为死锁的四个必要条件(Coffman条件),它们是:

  1. 互斥条件:一个资源每次只能被一个进程使用。
  2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
  3. 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺。
  4. 循环等待条件:系统中若干进程形成一种头尾相接的循环等待资源关系。

如果上述四个条件同时成立,系统就可能发生死锁。

操作系统层面的死锁最早在1965年由Dijkstra在研究银行家算法时提出,它是计算机操作系统乃至整个并发程序设计领域最难处理的问题之一。死锁的恢复和预防是操作系统的重要设计目标之一,常见的死锁预防和恢复策略包括鸵鸟算法(系统假装没有死锁发生)和死锁检测和恢复(系统并不试图阻止死锁的产生,而是允许死锁发生,当检测到死锁发生后,采取措施进行恢复)。

1.线程死锁

本节基于线程去讲解死锁。

有时,一个线程需要同时访问两个或更多不同的共享资源,而每个资源又都由不同的互斥量管理。当超过一个线程加锁同一组互斥量时,就有可能发生死锁。

两个或两个以上的进程在执行过程中,因争夺共享资源而造成的一种互相等待的现象若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁

死锁的几种场景:(1)忘记释放锁;(2)重复加锁;(3)多线程多锁,抢占锁资源

(2)连续两次(或多次)调用 pthread_mutex_lock 来锁定同一个互斥量(mutex)而没有相应的 pthread_mutex_unlock 调用会导致死锁(deadlock)。这是因为互斥量的设计初衷是确保同一时间只有一个线程可以访问被保护的资源或代码段。

当你第一次调用 pthread_mutex_lock(&mutex); 时,如果互斥量 mutex 已经被其他线程锁定,当前线程将会阻塞,直到互斥量被解锁。如果当前线程已经拥有了这个互斥量(即它之前已经成功锁定了这个互斥量但还没有解锁),那么再次调用 pthread_mutex_lock(&mutex); 会导致线程阻塞,因为它在等待自己释放互斥量,这是一个逻辑错误。

测试代码:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>// 创建2个互斥量
pthread_mutex_t mutex1, mutex2;void * workA(void * arg) {pthread_mutex_lock(&mutex1);sleep(1);pthread_mutex_lock(&mutex2);printf("workA....\n");pthread_mutex_unlock(&mutex2);pthread_mutex_unlock(&mutex1);return NULL;
}void * workB(void * arg) {pthread_mutex_lock(&mutex2);sleep(1);pthread_mutex_lock(&mutex1);printf("workB....\n");pthread_mutex_unlock(&mutex1);pthread_mutex_unlock(&mutex2);return NULL;
}int main() {// 初始化互斥量pthread_mutex_init(&mutex1, NULL);pthread_mutex_init(&mutex2, NULL);// 创建2个子线程pthread_t tid1, tid2;pthread_create(&tid1, NULL, workA, NULL);pthread_create(&tid2, NULL, workB, NULL);// 回收子线程资源pthread_join(tid1, NULL);pthread_join(tid2, NULL);// 释放互斥量资源pthread_mutex_destroy(&mutex1);pthread_mutex_destroy(&mutex2);return 0;
}

上述代码,最终执行不会输出任何东西。

首先,workA对1加锁,然后睡眠1秒。这个时候可能轮到B执行了,B先对2进行加锁,然后睡眠1秒。此时A醒了,尝试对2进行加锁,而2已经被B加锁了,因此A阻塞。轮到B的时候,对1进行加锁,然后1已经被A加上了,所以B阻塞等待。所有,发生了死锁。

2.读写锁

当有一个线程已经持有互斥锁时,互斥锁将所有试图进入临界区的线程都阻塞住。但是考虑一种情形,当前持有互斥锁的线程只是要读访问共享资源,而同时有其它几个线程也想读取这个共享资源,但是由于互斥锁的排它性,所有其它线程都无法获取锁,也就无法读访问共享资源了,但是实际上多个线程同时读访问共享资源并不会导致问题
在对数据的读写操作中,更多的是读操作,写操作较少,例如对数据库数据的读写应用。
为了满足当前能够允许多个读出但只允许一个写入的需求,线程提供了读写锁来实现。


◼ 读写锁的特点:
 如果有其它线程读数据,则允许其它线程执行读操作,但不允许写操作。
 如果有其它线程写数据,则其它线程都不允许读、写操作。
 写是独占的,写的优先级高。

如果A线程加的是读锁,然后B线程是要加写锁,C线程是要加读锁。那么由于写的优先级高,因此B先加写锁。

测试代码:

/*读写锁的类型 pthread_rwlock_tint pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);案例:8个线程操作同一个全局变量。3个线程不定时写这个全局变量,5个线程不定时的读这个全局变量
*/#include <stdio.h>
#include <pthread.h>
#include <unistd.h>// 创建一个共享数据,作为全局变量
int num = 1;// pthread_mutex_t mutex;互斥锁的效率相比于读写锁要慢
//创建读写锁
pthread_rwlock_t rwlock;void * writeNum(void * arg) {while(1) {pthread_rwlock_wrlock(&rwlock);num++;printf("++write, tid : %ld, num : %d\n", pthread_self(), num);pthread_rwlock_unlock(&rwlock);usleep(100);}return NULL;
}void * readNum(void * arg) {while(1) {pthread_rwlock_rdlock(&rwlock);printf("===read, tid : %ld, num : %d\n", pthread_self(), num);pthread_rwlock_unlock(&rwlock);usleep(100);}return NULL;
}int main() {pthread_rwlock_init(&rwlock, NULL);// 创建3个写线程,5个读线程pthread_t wtids[3], rtids[5];for(int i = 0; i < 3; i++) {pthread_create(&wtids[i], NULL, writeNum, NULL);}for(int i = 0; i < 5; i++) {pthread_create(&rtids[i], NULL, readNum, NULL);}// 设置线程分离for(int i = 0; i < 3; i++) {pthread_detach(wtids[i]);}for(int i = 0; i < 5; i++) {pthread_detach(rtids[i]);}pthread_exit(NULL);pthread_rwlock_destroy(&rwlock);return 0;
}

最后三行代码的原因是:如果destory在先,因为上面的detach运行后子线程可能没有运行完,所以可能会在子线程运行过程中destory互斥量,这样会出错,如果destory在后,那么主线程exit后也不会去destory互斥量,所以我建议上面回收子线程使用join,因为join是阻塞的,会等所有的子进程资源回收完了再继续,然后再destory互斥量,最后exit主线程。

也就是说:用pthread_join,最后,最初主线程pthread_exit(NULL)

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

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

相关文章

定频空调与变频空调的区别

变频空调是通过变频器来改变压缩机运转电压以及频率&#xff0c;从而改变压缩机转速的新型空调&#xff0c;那么他和定频空调有哪些区别呢&#xff1f; 一、控制启停的方式不同 定频空调控制方式为&#xff1a;当T环 < T设 - 2℃&#xff0c;压缩机停机。 当 T环 > T…

Java实现就医保险管理系统 JAVA+Vue+SpringBoot+MySQL

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 科室档案模块2.2 医生档案模块2.3 预约挂号模块2.4 我的挂号模块 三、系统展示四、核心代码4.1 用户查询全部医生4.2 新增医生4.3 查询科室4.4 新增号源4.5 预约号源 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVue…

【前端素材】推荐优质后台管理系统Qovex平台模板(附源码)

一、需求分析 1、定义 后台管理系统是一种用于管理和监控网站、应用程序或系统的在线工具。它通常是通过网页界面进行访问和操作&#xff0c;用于管理网站内容、用户权限、数据分析等。后台管理系统是网站或应用程序的控制中心&#xff0c;管理员可以通过后台系统进行各种管理…

构造函数,原型,实例,类的关系整理

视频来源js原型链、构造函数和类_哔哩哔哩_bilibili 如视频所说&#xff0c;构造函数的prototype指向原型&#xff0c;实例化的对象的__proto__指向原型&#xff0c;原型通过constructor指向构造函数&#xff0c;正如class里面的constructor方法就相当于Person构造函数一样&am…

基于PID-bang-bang控制算法的卫星姿态控制matlab仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于PID-bang-bang控制算法的卫星姿态控制。仿真输出控制器的控制收敛曲线&#xff0c;卫星姿态调整过程的动画。 2.系统仿真结果 3.核心程序与模型 版本&#xff1a;MATLAB…

连接SFTP服务器报错host key for has changed and you have requested strict checking

背景&#xff1a;今天连接SFTP的时候&#xff0c;报错如下&#xff0c;记录一下 报错如下 WARNING: REMOTE HOST IDENTIFICATION HSS CHANGED!cccececccccceeteeacaaeapepeppeeecpeepeeecepeeeaeeeeeceeeeeeIT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!Someone co…

LeetCode每日一题——2583. Kth Largest Sum in a Binary Tree

文章目录 一、题目二、题解 一、题目 You are given the root of a binary tree and a positive integer k. The level sum in the tree is the sum of the values of the nodes that are on the same level. Return the kth largest level sum in the tree (not necessaril…

前后端联调可能出现的问题

调不到后端数据 前后端传参方式不一样 --- formdata 主要接收文件 或者有文件和数据 --- json 纯数据

智能成绩表(C语言)

题目来自于博主算法大师的专栏&#xff1a;最新华为OD机试C卷AB卷OJ&#xff08;CJavaJSPy&#xff09; https://blog.csdn.net/banxia_frontend/category_12225173.html 题目描述 小明来到某学校当老师&#xff0c;需要将学生按考试总分或单科分数进行排名&#xff0c;你能帮…

springboot+vue项目部署配置开机自启动

1.前端部属 下载nginx解压&#xff0c;在nginx\conf下找到nginx.conf 添加如下代码 server {listen 8081;server_name localhost;charset utf-8;location / {root F:/1ceshi/dist; #前端打包路径try_files $uri $uri/ /index.html;index index.html index.htm;}l…

本地创建Git仓库

在 Windows 下&#xff0c;可以通过以下步骤在本地创建一个 并模拟远程Git 仓库。 1、在命令行中打开模拟远程Git 仓库目标文件夹&#xff1a; 打开命令提示符或 PowerShell。例如&#xff1a; 创建裸仓库&#xff08;模拟远程仓库&#xff09;&#xff1a;创建一个裸仓库&am…

Redis进阶篇

Redis线程模型 redis是基于内存运行的高性能k-v数据库&#xff0c;6.x之前是单线程, 对外提供的键值存储服务的主要流程 是单线程&#xff0c;也就是网络 IO 和数据读写是由单个线程来完成&#xff0c;6.x之后引入多线程而键值对读写命 令仍然是单线程处理的&#xff0c;所以 …

【PX4SimulinkGazebo联合仿真】在Simulink中使用ROS2控制无人机进入Offboard模式起飞悬停并在Gazebo中可视化

在Simulink中使用ROS2控制无人机进入Offboard模式起飞悬停并在Gazebo中可视化 系统架构Matlab官方例程Control a Simulated UAV Using ROS 2 and PX4 Bridge运行所需的环境配置PX4&Simulink&Gazebo联合仿真实现方法建立Simulink模型并完成基本配置整体框架各子系统实现…

Java爬虫使用JSoup获取静态资源图片

import org.jsoup.Connection; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import java.io.FileOutputStream;/*** 获取静态图片*/public class ImageDownloader {public static void main…

一分钟 由浅入深 学会Navigation

目录 1.官网正式概念 1.1 初认知 2.导入依赖 2.1 使用navigation 2.2 safe Args插件-> 传递数据时用 3.使用Navigation 3.1 搭建初始框架 3.2 确定action箭头的属性 3.3 为Activity添加NavHostFragment控件 3.4 NavController 管理应用导航的对象 3.5 数据传递(单…

Linux——基础IO

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、C语言IO1、写文件2、读文件3、stdin & stdout & stderr 二、系统文件I/O1、写文件…

常用显示屏学习——LCD12864(含高级驱动程序)

LCD12864液晶显示屏 屏幕介绍 ① 可显示四行字符&#xff0c;每行可显示8个汉字或者16个数字和字母&#xff1b; ②可串行通信和并行通信&#xff1b; ③ 串口接口管脚信号 通信方法 &#xff08;一&#xff09;八位并行通信方法 &#xff08;二&#xff09;串行通信方法 用…

基于springboot接口的编写

目录 1、模糊分页查询 2、批量删除 3、新增 4、编辑 此接口非彼接口。此接口是MVC的设计模式中的Controller层&#xff0c;一般我们会叫Controller层里的方法为接口。他们是负责接收前端或者其它服务的传来的请求&#xff0c;并对请求进行相应的处理&#xff0c;最终再将处…

工具方法合集-utils.js

通用 import get from lodash.get import cloneDeep from lodash.clonedeep // 深度clone export function deepClone(obj) {return obj ? cloneDeep(obj) : obj } export function lodashGet(obj, key, defaultValue ) {//这个 defaultValue 不能给默认 值 会报错&#xf…

超实用,分享PostgreSQL和mysql的几点区别

前言 今天是元宵节&#xff0c;首先祝大伙元宵快乐&#xff01;上一篇文章&#xff0c;给大家讲解了一下MySQL和PostgreSQL性能上的差别。这篇文章主要是记录一下日常应用中&#xff0c;两者常见的一些语法以及一些区别。 PostgreSQL的数据类型 数值类型 字符串类型 日期|时…