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

【Linux】线程同步和死锁

目录

死锁

什么是死锁

构成死锁的四个必要条件

如何避免死锁

线程同步

同步的引入

同步的方式

条件变量

条件变量的使用

整体代码


 

死锁

什么是死锁

        死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所占用不会释放 的资源而处于的一种永久等待状态.

        例如进程两个锁mtx1和mtx2,进程A执行某一段代码需要先申请mtx1,再申请mtx2;而进程B执行对应的代码需要现申请mtx2,再申请mtx1.

        某个时刻,进程A和B同时运行,进程A拿到了mtx1,进程B拿到了mtx2,但紧接着,进程A需要mtx2,但此时这把锁被进程B所占用,无法申请,便阻塞等待进程B的完成。而进程B需要mtx1,但是此时被A占用,需要等待进程A的完成,也阻塞在了这里。于是便造成了死锁。

        

构成死锁的四个必要条件

  • 互斥条件: 一个资源每次只能被一个执行流使用,其他进程无法同时访问该资源。
  • 请求与保持条件:即进程在请求资源时可以保持已占有的资源,即不释放自己原本的资源
  • 不剥夺条件:已经获得资源的进程不能被强行剥夺其他进程所拥有的资源,只有自愿释放
  • 循环等待条件:若干进程之间形成一种头尾相连的循环等待资源关系。如A->B,B->A.

如何避免死锁

     我们知道了构成死锁的那个四个必要条件,只要破坏其中任意一个 条件即可:

  • 破坏互斥条件:尽量避免使用互斥资源,或者采用不同的资源访问方式,如读写锁,允许多个进程或线程同时访问某些资源。
  • 破坏请求与保持条件:如果申请多个锁失败,则释放自己已经拥有的资源
  • 破坏不剥夺条件:引入资源抢占机制,即允许操作系统对进程已获得的资源进行抢占。当其他进程紧迫需要某个资源时,系统可以终止或暂停某个进程,将其持有的资源释放分配给需要的进程。
  • 打破环路等待条件:采用全局资源排序策略,为每个资源指定一个唯一的编号,然后要求进程只能按照编号递增的顺序请求资源,这样可以避免环路等待的发生。

   以上所说的"资源"都也可以理解为锁。

线程同步

同步的引入

        上一章我们说的        

        1.多线程然后抢票的例子,我们发现虽然有多个线程,但是每一次基本上都是那一个线程在抢(比如优先级可能更高),其它线程抢不到,这就是一个线程频繁地申请到资源,造成别的线程饥饿问题。

        2.假设一个资源暂时没有了,而线程依旧在竞争锁,然后访问资源,访问不到然后释放锁没就这样一直进行,但此时也没有资源可用。这样就太过于浪费了。

        以上这些操作都是正确的,但是是不合理的!

        所以为了解决上面这系列问题,便引入了同步:主要是为了解决 访问临界资源合理性问题的.

按照一定的顺序,进行临界资源的访问

        1.对于问题一我们可以这样:当一个线程申请到资源后,使用完之后,排到其它线程后面,让其他线程先访问,如此进行下去。

        2.对于问题二,我们可以暂时每个线程发个号,当有资源时,再按照号的顺序来访问资源,而不是互相不正当竞争这份资源

所以线程同步的是:线程同步是指在并发编程中,通过协调多个线程的执行顺序以及对共享资源的访问来保证线程之间的正确交互

同步的方式

条件变量

        当我们申请临界资源时 ---> 要对临界资源是否存在做检测 ---> 检测的本质:也是访问临界资源 --->结论:对临界资源的检测,也一定是在加锁和解锁之间的。

        既然这样,那检测依然需要频繁地申请和释放锁,那么有没有办法让线程检测到资源不就绪的时候:

        a.不要让线程自己再频繁检测了,而是等待

        b.当条件就绪的时候,通知对应的线程,让它来进行资源的申请与访问。

为了满足上面的说话,这里就有引入条件变量。

        条件变量(Condition Variable)是一种同步原语,常用于多线程编程中进行线程间的等待和通知。它用于实现线程之间的同步和协作,使得一个线程可以等待某个条件的满足并被其他线程通知唤醒。

条件变量的使用

        使用条件变量需要配合互斥锁(pthread_mutex_t)来保证线程的安全操作。一般的使用步骤如下:

        首先我们要先创建一个条件变量,数据类型为 pthread_cond_t,同时也要创建互斥锁。

        

    pthread_mutex_t mtx;pthread_cond_t cond;

       初始化条件变量和互斥锁:互斥锁我们说了初始化方式了,这里说初始化条件变量的函数:

 pthread_cond_init,该函数原型如下:

       int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);

        第一个参数为条件变量,第二个参数为条件变量属性,一般设为NULL。

        如果创建的条件变量是全局的,那么可以用下面的方法进行初始化:

       pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

所以初始化代码如下: 

    pthread_cond_init(&cond,nullptr);//初始化条件变量pthread_mutex_init(&mtx,nullptr);//初始化锁

紧接着,我们创建4个线程,然后再创建一个结构体,里面包含了线程名,该线程调用的方法,条件变量和互斥锁,然后编写一个构造函数来初始化这些:

typedef void(*func_t)(const string& name,pthread_mutex_t* pmtx, pthread_cond_t* pcond);
class ThreadData
{
public:ThreadData(const string& name,func_t func,pthread_mutex_t* pmtx, pthread_cond_t* pcond):_name(name),_func(func),_pmtx(pmtx),_pcond(pcond){}
public:string _name;//线程名func_t _func;//该线程对应的回调方法pthread_mutex_t* _pmtx;//互斥锁pthread_cond_t* _pcond;//条件变量
};

        然后开始编写每个线程的回调方法,这里其实不能很好地展示条件变量中锁的作用,我们需要在下一章生产者与消费者模型时,才能好好看出作用.

        这里每个线程方法,我们先利用pthread_cond_wait进行阻塞等待,当资源准备就绪的时候,才会继续向后执行。然后后面输出一条语句,

        该函数原型如下:

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

        函数pthread_cond_wait用于使线程进入等待状态,并且会原子性地释放由mutex指定的互斥锁,进入等待状态后会阻塞等待,直到其他线程使用相同的条件变量调用pthread_cond_signal或pthread_cond_broadcast时,被唤醒并重新获得互斥锁。调用前需要确保已经加锁。返回时会重新获得互斥锁。

        其中第一个参数为条件变量,第二个参数为互斥锁。具体什么作用,下节课会讲。

        这是线程1的回调方法,线程2,3,4都是同样地,只不过名字不同。

void func1(const string& name,pthread_mutex_t* pmtx, pthread_cond_t* pcond)
{while(true){pthread_mutex_lock(pmtx);//if(临界资源不就绪) wait之前一般会进行检测,这里由于无法很好的模拟场景,就暂时不加ifpthread_cond_wait(pcond,pmtx);//默认该线程在执行的时候,wait代码被执行,当前线程会立即被阻塞cout << name << "running ..." << endl;pthread_mutex_unlock(pmtx);}
}

        现在创建好线程后,每个线程都被阻塞在了pthread_mutex_wait接口这里,所以我们需要再主函数中唤醒这些线程,共有两种方式:

pthread_cond_signal

int pthread_cond_signal(pthread_cond_t *cond);

参数为条件变量,至于唤醒哪一个线程,这是由调度器决定的,但顺序一定是固定的,当我们运行起来程序后:

        这样便保证了线程同步。 

这是一个一个线程的唤醒,如果我们想唤醒所有线程,这就需要用到pthread_cond_broadcast,

该函数原型如下:

int pthread_cond_broadcast(pthread_cond_t *cond);

参数同样也为条件变量,函数pthread_cond_broadcast用于广播条件变量的信号,即唤醒所有等待此条件变量的线程。

 这样便一次能唤醒所有线程继续执行了。


一切完成之后,我们需要在最后销毁释放条件变量和互斥锁,函数为pthread_cond_destroy,

函数原型为:

       int pthread_cond_destroy(pthread_cond_t *cond);

参数同样为定义的条件变量,传进去之后,即可释放条件变量。


整体代码

        以上便是条件变量的一个大致使用流程,具体的理解下一章生产者消费者模型会讲解,这列理解了条件变量的用法即可。

        可以拷贝到自己平台下运行,编译时记得加上-lpthread,如下:

g++ -o mythread mythread.cc -lpthread

代码:

#include<iostream>
#include<string>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>using namespace std;#define PTHREAD_NUM 4typedef void(*func_t)(const string& name,pthread_mutex_t* pmtx, pthread_cond_t* pcond);
class ThreadData
{
public:ThreadData(const string& name,func_t func,pthread_mutex_t* pmtx, pthread_cond_t* pcond):_name(name),_func(func),_pmtx(pmtx),_pcond(pcond){}
public:string _name;func_t _func;pthread_mutex_t* _pmtx;pthread_cond_t* _pcond;
};void func1(const string& name,pthread_mutex_t* pmtx, pthread_cond_t* pcond)
{while(true){pthread_mutex_lock(pmtx);//if(临界资源不就绪) wait之前一般会进行检测,这里由于无法很好的模拟场景,就暂时不加ifpthread_cond_wait(pcond,pmtx);//默认该线程在执行的时候,wait代码被执行,当前线程会立即被阻塞cout << name << "running ..." << endl;pthread_mutex_unlock(pmtx);}
}
void func2(const string& name,pthread_mutex_t* pmtx, pthread_cond_t* pcond)
{while(true){pthread_mutex_lock(pmtx);pthread_cond_wait(pcond,pmtx);//默认该线程在执行的时候,wait代码被执行,当前线程会立即被阻塞cout << name << "running ..." << endl;pthread_mutex_unlock(pmtx);}
}
void func3(const string& name,pthread_mutex_t* pmtx, pthread_cond_t* pcond)
{while(true){pthread_mutex_lock(pmtx);pthread_cond_wait(pcond,pmtx);//默认该线程在执行的时候,wait代码被执行,当前线程会立即被阻塞cout << name << "running ..." << endl;pthread_mutex_unlock(pmtx);}
}
void func4(const string& name,pthread_mutex_t* pmtx, pthread_cond_t* pcond)
{while(true){pthread_mutex_lock(pmtx);pthread_cond_wait(pcond,pmtx);//默认该线程在执行的时候,wait代码被执行,当前线程会立即被阻塞cout << name << "running ..." << endl;pthread_mutex_unlock(pmtx);}
}void* Entry(void* args)
{ThreadData* td = (ThreadData*)args;//td在每一个线程自己私有的栈空间保存td->_func(td->_name,td->_pmtx,td->_pcond);delete td;return nullptr;
}
int main()
{pthread_mutex_t mtx;pthread_cond_t cond;pthread_mutex_init(&mtx,nullptr);pthread_cond_init(&cond,nullptr);pthread_t tids[PTHREAD_NUM];//定义四个线程的回调方法func_t funcs[PTHREAD_NUM] = {func1,func2,func3,func4};for(int i = 0; i < PTHREAD_NUM; i++){string name = "thread ";name += to_string(i+1);ThreadData* td = new ThreadData(name,funcs[i],&mtx,&cond);pthread_create(tids+i,nullptr,Entry,(void*)td);}//这里为了方便演示pthread_cond_wait,在没有用signal或broadcast唤醒前,一直处于阻塞状态sleep(5);//控制线程while(true){cout << "wake up thread run code ..." << endl;//pthread_cond_signal(&cond);//唤醒一个线程pthread_cond_broadcast(&cond);//唤醒全部线程sleep(1);}for(int i = 0; i < PTHREAD_NUM; i++){pthread_join(tids[i],nullptr);}pthread_mutex_destroy(&mtx);pthread_cond_destroy(&cond);return 0;
}

相关文章:

【Linux】线程同步和死锁

目录 死锁 什么是死锁 构成死锁的四个必要条件 如何避免死锁 线程同步 同步的引入 同步的方式 条件变量 条件变量的使用 整体代码 死锁 什么是死锁 死锁是指在一组进程中的各个进程均占有不会释放的资源&#xff0c;但因互相申请被其他进程所占用不会释放 的资源而处…...

Matplotlib数据可视化(二)

目录 1.rc参数设置 1.1 lines.linestype取值 1.2 lines.marker参数的取值 1.3 绘图中文预设 1.4 示例 1.4.1 示例1 1.4.2 示例2 1.rc参数设置 利用matplotlib绘图时为了让绘制出的图形更加好看&#xff0c;需要对参数进行设置rc参数设置。可以通过以下代码查看matplotli…...

图像去雨-雨线清除-图像处理-(计算机作业附代码)

背景 多年来&#xff0c;图像去雨已经被广泛研究&#xff0c;使用传统方法和基于学习的方法。然而&#xff0c;传统方法如高斯混合模型和字典学习方法耗时&#xff0c;并且无法很好地处理受到严重雨滴影响的图像块。 算法 通过考虑雨滴条状特性和角度分布&#xff0c;这个问…...

pycharm调整最大堆发挥最大

python程序运行时&#xff0c;怎么提高效率&#xff0c;设置pycharm最大堆过程如下&#xff1b; 一、进入设置pycharm最大堆&#xff1b; 二、进入设置pycharm最大堆&#xff1b; 如果8g设置为6g左右&#xff0c;占75%左右最佳...

uni-app 经验分享,从入门到离职(二)—— tabBar 底部导航栏实战基础篇

文章目录 &#x1f4cb;前言⏬关于专栏 &#x1f3af;关于小程序 tabbar 的一些知识&#x1f3af;创建一个基本的 tabBar&#x1f4dd;最后 &#x1f4cb;前言 这篇文章的内容主题是关于小程序的 tabBar 底部导航栏的入门使用和实战技巧。通过上一篇文章的基础&#xff0c;我们…...

【李沐】3.2线性回归从0开始实现

%matplotlib inline import random import torch from d2l import torch as d2l1、生成数据集&#xff1a; 看最后的效果&#xff0c;用正态分布弄了一些噪音 上面这个具体实现可以看书&#xff0c;又想了想还是上代码把&#xff1a; 按照上面生成噪声&#xff0c;其中最后那…...

一百五十六、Kettle——Linux上安装的Kettle9.3连接ClickHouse数据库(亲测,附流程截图)

一、目标 kettle9.3在Linux上安装好后&#xff0c;需要与ClickHouse数据库建立连接 二、前提准备 &#xff08;一&#xff09;在Linux已经安装好kettle并可以启动kettle &#xff08;二&#xff09;已知kettle和ClickHouse版本 1、kettle版本是9.3 2、ClickHouse版本是21…...

图数据库_Neo4j和SpringBoot整合使用_创建节点_删除节点_创建关系_使用CQL操作图谱---Neo4j图数据库工作笔记0009

首先需要引入依赖 springboot提供了一个spring data neo4j来操作 neo4j 可以看到它的架构 这个是下载下来的jar包来看看 有很多cypher对吧 可以看到就是通过封装的驱动来操作graph database 然后开始弄一下 首先添加依赖...

Uniapp连接蓝牙设备

一、效果图 二、流程图 三、实现 UI <uni-list><uni-list :border="true"><!-- 显示圆形头像 -->...

linux切换到root用户:su root和sudo su命令的区别

前言 工作过程中遇到需要切换到root用户下去执行命令 方法1&#xff1a;工作中常会选择这个方法 利用su root命令 临时获取root用户权限&#xff0c;工作目录不变 好处&#xff1a;不需要知道root用户的密码&#xff0c;直接输入普通用户的密码即可 方法2 利用sudo su命…...

kafka-- kafka集群 架构模型职责分派讲解

一、 kafka集群 架构模型职责分派讲解 生产者将消息发送到相应的Topic&#xff0c;而消费者通过从Topic拉取消息来消费 Kafka奇数个节点消费者consumer会将消息拉去过来生产者producer会将消息发送出去数据管理 放在zookeeper...

Effective C++条款07——为多态基类声明virtual析构函数(构造/析构/赋值运算)

有许多种做法可以记录时间&#xff0c;因此&#xff0c;设计一个TimeKeeper base class和一些derived classes 作为不同的计时方法&#xff0c;相当合情合理&#xff1a; class TimeKeeper { public:TimeKeeper();~TimeKeeper();// ... };class AtomicClock: public TimeKeepe…...

用友Java后端笔试2023-8-5

计算被直线划分区域 在笛卡尔坐标系&#xff0c;存在区域[A,B],被不同线划分成多块小的区域&#xff0c;简单起见&#xff0c;假设这些不同线都直线并且不存在三条直线相交于一点的情况。 img 那么&#xff0c;如何快速计算某个时刻&#xff0c;在 X 坐标轴上[ A&#xff0c;…...

idea2023 springboot2.7.5+mybatis+jsp 初学单表增删改查

创建项目 因为2.7.14使用量较少&#xff0c;特更改spring-boot为2.7.5版本 配置端口号 打开Sm01Application类&#xff0c;右键运行启动项目&#xff0c;或者按照如下箭头启动 启动后&#xff0c;控制台提示如下信息表示成功 此刻在浏览器中输入&#xff1a;http://lo…...

大语言模型之四-LlaMA-2从模型到应用

最近开源大语言模型LlaMA-2火出圈&#xff0c;从huggingface的Open LLM Leaderboard开源大语言模型排行榜可以看到LlaMA-2还是非常有潜力的开源商用大语言模型之一&#xff0c;相比InstructGPT&#xff0c;LlaMA-2在数据质量、培训技术、能力评估、安全评估和责任发布方面进行了…...

Android 远程真机调研

背景 现有的安卓测试机器较少&#xff0c;很难满足 SDK 的兼容性测试及线上问题&#xff08;特殊机型&#xff09;验证&#xff0c;基于真机成本较高且数量较多的前提下&#xff0c;可以考虑使用云测平台上的机器进行验证&#xff0c;因此需要针对各云测平台进行调研、比较。 …...

B. 攻防演练 (2021CCPC女生赛)

题意&#xff1a; 给出一个长度为n的字符&#xff0c;字符是前m个小写字母&#xff0c;有q个询问&#xff0c;每次询问一个最短子序列的长度满足不是[l,r]内任意一个子序列 思路&#xff1a; [l,r]中子序列可以看成是从[l,r]中的某个位置开始&#xff0c;跳到下一个字符的位…...

MAC环境,在IDEA执行报错java: -source 1.5 中不支持 diamond 运算符

Error:(41, 51) java: -source 1.5 中不支持 diamond 运算符 (请使用 -source 7 或更高版本以启用 diamond 运算符) 进入设置 修改java版本 pom文件中加入 <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin&l…...

Tomcat日志中文乱码

修改安装目录下的日志配置 D:\ProgramFiles\apache-tomcat-9.0.78\conf\logging.properties java.util.logging.ConsoleHandler.encoding GBK...

最小生成树 — Prim算法

同Kruskal算法一样&#xff0c;Prim算法也是最小生成树的算法&#xff0c;但与Kruskal算法有较大的差别。 Prim算法整体是通过“解锁” “选中”的方式&#xff0c;点 -> 边 -> 点 -> 边。 因为是最小生成树&#xff0c;所以针对的也是无向图&#xff0c;所以可以随意…...

如何使用PHP Smarty模板进行AJAX交互?

首先&#xff0c;我们要明白&#xff0c;AJAX是一种在无需刷新整个页面的情况下&#xff0c;与服务器进行通信的技术。这对于改善用户体验来说&#xff0c;是个大宝贝。而PHP Smarty模板则是PHP的一种模板引擎&#xff0c;它使得设计和开发人员能够更好地分离逻辑和显示。 现在…...

nginx反向代理、负载均衡

修改nginx.conf的配置 upstream nginx_boot{# 30s内检查心跳发送两次包&#xff0c;未回复就代表该机器宕机&#xff0c;请求分发权重比为1:2server 192.168.87.143 weight100 max_fails2 fail_timeout30s; server 192.168.87.1 weight200 max_fails2 fail_timeout30s;# 这里的…...

React Native文本添加下划线

import { StyleSheet } from react-nativeconst styles StyleSheet.create({mExchangeCopyText: {fontWeight: bold, color: #1677ff, textDecorationLine: underline} })export default styles...

微服务-Nacos(配置管理)

配置更改热更新 在Nacos中添加配置信息&#xff1a; 在弹出表单中填写配置信息&#xff1a; 配置获取的步骤如下&#xff1a; 1.引入Nacos的配置管理客户端依赖&#xff08;A、B服务&#xff09;&#xff1a; <!--nacos的配置管理依赖--><dependency><groupId&…...

UML图绘制 -- 类图

1.类图的画法 类 整体是个矩形&#xff0c;第一层类名&#xff0c;第二层属性&#xff0c;第三层方法。 &#xff1a;public- : private# : protected空格: 默认的default 对应的类写法。 public class Student {public String name;public Integer age;protected I…...

SAP ME2L/ME2M/ME3M报表增强添加字段(包含:LMEREPI02、SE18:ES_BADI_ME_REPORTING)

ME2L、ME2M、ME3M这三个报表的字段增强&#xff0c;核心点都在同一个结构里 SE11:MEREP_OUTTAB_PURCHDOC 在这里加字段&#xff0c;如果要加的字段是EKKO、EKPO里的数据&#xff0c;直接加进去&#xff0c;啥都不用做&#xff0c;就完成了 如果要加的字段不在EKKO和EKPO这两个…...

探讨uniapp的数据缓存问题

异步就是不管保没保存成功&#xff0c;程序都会继续往下执行。同步是等保存成功了&#xff0c;才会执行下面的代码。使用异步&#xff0c;性能会更好&#xff1b;而使用同步&#xff0c;数据会更安全。 1 uni.setStorage(OBJECT) 将数据存储在本地缓存中指定的 key 中&#x…...

服务的拆分

纵向拆分 是从业务维度进行拆分。标准是按照业务的关联程度来决定&#xff0c;关联比较密切的业务适合拆分为一个微服务&#xff0c;而功能相对比较独立的业务适合单独拆分为一个微服务。 以社交App为例&#xff0c;你可以认为首页信息流是一个服务&#xff0c;评论是一个服务…...

Uniapp Syntax Error: Error: Unbalanced delimiter found in string

报错 in ./src/pages/user/components/tasks.vue?vue&typescript&langjs&Syntax Error: Error: Unbalanced delimiter found in string...这边导致文件的原因&#xff1a;可能是条件编译语法不小心删了某个字符&#xff0c;导致不全&#xff0c;无法形成一对。 //…...

视频集中存储EasyCVR视频汇聚平台定制项目增加AI智能算法

安防视频集中存储EasyCVR视频汇聚平台&#xff0c;可支持海量视频的轻量化接入与汇聚管理。平台能提供视频存储磁盘阵列、视频监控直播、视频轮播、视频录像、云存储、回放与检索、智能告警、服务器集群、语音对讲、云台控制、电子地图、平台级联、H.265自动转码等功能。为了便…...