9.【Linux】(死锁问题||线程同步||条件变量||生产者消费者模型)

常见锁的概念

死锁

死锁是指在一组进程中各个进程均占有不会释放的资源,但因互相申请被其他进程所占用的不会释放的资源而处于一种永久等待的状态。简单来说就是两个线程都
在等待对方释放锁

小林coding的例子

死锁必要条件

必须同时满足四个条件才会产生死锁

1.互斥条件:一个资源每次只能被一个执行流使用
2.请求与保持条件(持有并等待条件):一个执行流因请求资源而阻塞时,对已获得的资源保持不放。
3.不可剥夺条件:一个执行流已获得的资源,在未使用完之前,不能强行剥夺。
4.环路等待条件:若干执行流之间形成一种头尾详解的循环等待条件。

解决死锁问题(资源有序分配)

破坏4个必要条件之一即可,最常见的并且可行的就是使用资源有序分配法,来破环环路等待条件。
那什么是资源有序分配法呢?
线程 A 和 线程 B 获取资源的顺序要一样,当线程 A 是先尝试获取资源 A,然后尝试获取资源 B 的时候,线程 B 同样也是先尝试获取资源 A,然后尝试获取资源 B。也就是说,线程 A 和 线程 B 总是以相同的顺序申请自己想要的资源。

其他方法

死锁检测算法、银行家算法。

线程同步

同步问题是保证数据安全的情况下,让线程访问资源具有一定的顺序性从而避免饥饿问题。如果没有适当的同步机制,就可能出现数据不一致、数据竞态条件(race condition)或其他不可预测的行为。

条件变量

允许线程等待某个条件成立(通常与互斥锁一起使用)。当条件不满足时,线程会阻塞并等待其他线程发出信号。它是在多线程程序中用来实现“等待–>唤醒”逻辑常用的方法。它用于维护一个条件,这个条件与条件变量是不同的概念。线程可以使用条件变量来等待某个条件为真,注意这里并不是等待条件变量为真。它允许线程在等待某个条件成立时进入休眠状态,从而避免了不必要的CPU资源浪费,提高了程序的效率和响应速度.

相关函数

初始化

int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
参数:
cond:要初始化的条件变量
attr:NULL

销毁

int pthread_cond_destroy(pthread_cond_t *cond)

等待条件满足

int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
参数:
cond:要在这个条件变量上等待
mutex:互斥量,后面详细解释

????为何要带锁
在让线程在条件变量时等待时,会自动释放传入的锁。当被唤醒该函数返回时会重新持有锁。

如何判断临界资源是否就绪?通过访问临界资源,也必须加锁。等待过程所以必须在加锁和解锁之间。

唤醒等待

int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);

signal唤醒等待在该条件变量的一个线程,broadcast唤醒等待在该条件变量下的所有线程。

生产者消费者模型

生产者消费者模型(Producer-Consumer Model)是一种常用的并发编程模型,主要用于解决生产数据(或任务)和消费数据(或任务)之间的平衡问题。
生产者(Producer):主要负责生成数据(或任务),并将其放入一个共享的数据缓冲区(如队列、栈等)。当缓冲区满时,生产者通常会等待,直到缓冲区中有空间可以存放新的数据。
消费者(Consumer):主要负责从共享的数据缓冲区中取出数据(或任务)并进行处理。当缓冲区为空时,消费者通常会等待,直到缓冲区中有新的数据可供消费。

基于阻塞队列的CP模型

block.hpp

   #pragma once #include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<queue>
#include<mutex>template<class T>
class Blockq{static const int default_num=20;
public:Blockq(int maxcap=default_num):maxcap_(maxcap){pthread_mutex_init(&mutex_,nullptr);pthread_cond_init(&p_cond_,nullptr);pthread_cond_init(&c_cond_,nullptr);low_water_=maxcap_/3;high_water_=(maxcap_*2)/3;}T pop(){pthread_mutex_lock(&mutex_);while(q_.size()==0)//防止伪唤醒,用while判断{pthread_cond_wait(&c_cond_,&mutex_);}T out=q_.front();q_.pop();if(q_.size()<low_water_)pthread_cond_signal(&p_cond_);pthread_mutex_unlock(&mutex_);return out;}void push(const T& in){pthread_mutex_lock(&mutex_);while(q_.size()==maxcap_){pthread_cond_wait(&p_cond_,&mutex_);}q_.push(in);if(q_.size()>high_water_)pthread_cond_signal(&c_cond_);pthread_mutex_unlock(&mutex_);}~Blockq(){pthread_mutex_destroy(&mutex_);pthread_cond_destroy(&c_cond_);pthread_cond_destroy(&p_cond_);}private:std::queue<T> q_;int maxcap_;pthread_mutex_t mutex_;pthread_cond_t p_cond_;pthread_cond_t c_cond_;int low_water_;int high_water_;
};

main.cc

#include"BlockQueue.hpp"
#include"Task.hpp"
#include<ctime>
#include<unordered_map>std::string ops="+-*/%";
void * Consumer(void* args)
{Blockq<Task>* bq=static_cast<Blockq<Task>*>(args);while(true){//消费Task t=bq->pop();std::cout<<pthread_self()<<" :"<<"消费一个任务:";t.run();}
}void * Productor(void* args)
{Blockq<Task>* bq=static_cast<Blockq<Task>*>(args);while(true){int data1=rand()%10+1;int data2=rand()%10+1;char op=ops[rand()%5];Task t(data1,data2,op);usleep(10);bq->push(t);std::cout<<"生产了一个任务:"<<std::endl;}
}int main()
{srand(time(nullptr));Blockq<Task>* bq=new Blockq<Task>();pthread_t c[3],p[5];for(int i=0;i<3;i++){pthread_create(c+i,nullptr,Consumer,bq);}for(int i=0;i<5;i++){pthread_create(p+i,nullptr,Productor,bq);}for(int i=0;i<3;i++){pthread_join(c[i],nullptr);}for(int i=0;i<5;i++){pthread_join(p[i],nullptr);}delete bq;return 0;
}

task.hpp

#pragma once 
#include<iostream>class Task{
private:int _a;int _b;char _op;int _result;
public:Task(int a,int b,char op):_a(a),_b(b),_op(op),_result(0){}void run(){switch (_op){case '+':_result=_a+_b;break;case '-':_result=_a-_b;break;case '*':_result=_a*_b;break;case '/':_result=_a/_b;break;case '%':_result=_a%_b;break;default:break;}std::cout<<_a<<_op<<_b<<"="<<_result<<std::endl;}
};

makefile

blockqueue:main.ccg++ -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:rm -rf blockqueue

基于信号量和环形队列的CP模型

见上篇博客

读者写者模型

读者写者模型是操作系统中的一种同步与互斥机制,主要用于处理对共享资源的并发访问问题。在这个模型中,存在两种角色:读者和写者,他们共享一个读写场所。
读者是对共享资源进行读取操作的一方,多个读者可以同时并发地读取资源,这是因为读操作并不改变资源的内容,因此读者之间是共享关系。而写者则是进行写操作的一方,写操作会修改共享资源的内容,因此具有排他性,即同一时刻只能有一个写者进行写操作,写者之间是互斥关系

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

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

相关文章

在做题中学习(48):朴素的二分查找

. - 力扣&#xff08;LeetCode&#xff09; 解法一&#xff1a; 暴力求解 for循环中&#xff0c;从nums[0]枚举到nums[n-1]&#xff0c;依次判断&#xff0c;返回 target的值。 时间复杂度 : O(N) :因为要遍历一遍数组 解法二&#xff1a;二分查找 因为此数组为有序的…

AI大模型探索之路-训练篇9:大语言模型Transformer库-Pipeline组件实践

系列篇章&#x1f4a5; AI大模型探索之路-训练篇1&#xff1a;大语言模型微调基础认知 AI大模型探索之路-训练篇2&#xff1a;大语言模型预训练基础认知 AI大模型探索之路-训练篇3&#xff1a;大语言模型全景解读 AI大模型探索之路-训练篇4&#xff1a;大语言模型训练数据集概…

MT3608B 航天民芯代理 1.2Mhz 24V输入 升压转换器

深圳市润泽芯电子有限公司为航天民芯一级代理商 技术支持欢迎试样~Tel&#xff1a;18028786817 简述 MT3608B是恒定频率的6针SOT23电流模式升压转换器&#xff0c;用于小型、低功耗应用。MT3608B开关频率为1.2MHz&#xff0c;允许使用微小、低电平成本电容器和电感器高度不…

托普利兹矩阵(T矩阵)及其应用(Matlab demo测试)

托普利兹矩阵&#xff08;T矩阵&#xff09;及其应用&#xff08;Matlab demo测试&#xff09; 1. 概念2. Matlab简单测试2.1 生成测试2.2 基本性质及原理2.3 性质验证 3. 其他应用总结3.1 其他性质3.2 文献阅读看到的 参考资料 1. 概念 托普利兹矩阵&#xff0c;简称为T型矩阵…

深入探索计算机视觉:高级主题与前沿应用的全面解析

引言 计算机视觉&#xff0c;作为人工智能领域的一个重要分支&#xff0c;旨在让计算机能够“看”懂世界&#xff0c;理解和解释视觉场景。随着深度学习技术的迅猛发展&#xff0c;计算机视觉已经在许多领域取得了显著的进展&#xff0c;如自动驾驶、安防监控、医疗诊断等。在…

PG后台进程个人解读和与oracle 的比较

1.background writer &#xff08;后台写进程&#xff09; 与OracleDBWR进程工作原理类似&#xff0c;都是负责把缓冲区里面的脏块写到数据文件中&#xff0c;写的目的有两个&#xff1a; 1.为了保存数据。 2.为了释放内存空间。 触发background writer 写的条件&#xff1a…

【C++程序员的自我修炼】string 库中常见的用法 (一)

唤起一天明月照我满怀冰雪浩荡百川流鲸饮未吞海 剑气已横秋 目录 string 库的简介 string 的一些小操作 构造函数的使用 拷贝构造的常规使用 指定拷贝内容的拷贝构造 拷贝字符串开始的前 n 个字符 用 n 个字符初始化 计算字符串的长度 string 的三种遍历方式 常规的for循环 op…

低空经济+无人机:细分赛道万亿级别市场崛起,可能成为年内最大热点

近年来&#xff0c;低空经济作为一种新兴的产业&#xff0c;正在快速发展。特别是在无人机的快速普及下&#xff0c;低空经济已经展现出巨大的潜力和市场前景。无人机从狭窄的“会飞的相机”逐渐进入到更多场景&#xff0c;从消费市场进入到工业市场&#xff0c;不断拓展其应用…

AJAX家政系统 自营+多商家(高级授权)+独立端口 -源码下载

应用介绍 后台&#xff1a;https://service.hnajax.com/hxeJVakAdf.php/index/login AJAX家政系统 自营多商家(高级授权)独立端口 基于FastAdmin和原生微信小程序开发的一款同城预约、上门服务、到店核销家政系统&#xff0c;用户端、服务端(高级授权)、门店端(高级授权)各端…

如何使用Go语言进行并发安全的数据访问?

文章目录 并发安全问题的原因解决方案1. 使用互斥锁&#xff08;Mutex&#xff09;示例代码&#xff1a; 2. 使用原子操作&#xff08;Atomic Operations&#xff09;示例代码&#xff1a; 3. 使用通道&#xff08;Channels&#xff09; 在Go语言中&#xff0c;进行并发编程是常…

综合性练习(后端代码练习3)——留言板

目录 一、准备工作 二、约定前后端交互接口 1、需求分析 2、接口定义 &#xff08;1&#xff09;发布留言 &#xff08;2&#xff09;获取留言 三、实现服务器代码 1、lombok介绍 &#xff08;1&#xff09;引入依赖 &#xff08;2&#xff09;使用lombok &#xff…

OI Wiki—枚举

//新生训练&#xff0c;搬运整理 简介 枚举&#xff08;英语&#xff1a;Enumerate&#xff09;是基于已有知识来猜测答案的问题求解策略。 枚举的思想是不断地猜测&#xff0c;从可能的集合中一一尝试&#xff0c;然后再判断题目的条件是否成立。 要点 给出解空间 建立简洁…