Linux-----信号量

article/2025/11/6 20:39:14

信号量

  • 信号量原理
  • 信号量概念
  • 信号量函数
  • 基于环形队列的生产消费模型
  • 空间和数据资源
  • 生产者和消费者申请、释放信号量
  • 模拟实现基于环形队列的生产者消费者模型

信号量原理

  • 之前我们知道被多个执行流同时访问的公共资源叫做临界资源,而临界资源不保护的话会造成数据不一性的问题。
  • 之前我们用互斥锁保护临界资源是把这个临界资源当做一个整体,只能让1个执行流访问临界资源。现在我们把临界资源分割成多个区域,当多个执行流访问不同的区域,此时不会出现数据不一性的问题了。

信号量概念

信号量本质就是一个计数器,描述临界资源有效个数的计数器。

每个执行流先申请信号量,申请到信号量后同时访问临界资源,访问完后释放信号量。
在这里插入图片描述
信号量的PV操作:
P操作:我们将申请信号量的操作叫做P操作,申请信号量的本质就是有权限访问临界资源,申请成功后,P操作的本质就是让计数器–即可
V操作:将释放信号量叫做V操作,归还临界资源,V操作的本质是让计数器++

PV操作必须是原子的

执行流要申请信号量要先看到信号量,所以信号量本身就是临界资源。信号量是保护临界资源的,我们不能再用信号量去保护信号量,所以信号量的操作必须是原子的

申请信号量失败挂起等待

当执行流申请信号量时,可能此时信号量为0,说明信号量描述的临界资源被申请完了,那么这个执行流就要挂起等待,在信号量等待队列中等待,直到有信号量释放被唤醒

为什么使用信号量?

这样可以把临界资源分成多分,多执行流并行执行,提高了效率。

如何使用信号量呢?下面来看看信号量的一些函数

信号量函数

初始化信号量

int sem_init(sem_t *sem, int pshared, unsigned int value);

参数说明:
pshared:0表示线程间共享,非零表示进程间共享
value:信号量初始值

销毁信号量

int sem_destroy(sem_t *sem);

等待信号量

int sem_wait(sem_t *sem); //P()

等待信号量,会将信号量的值-1

释放信号量

int sem_post(sem_t *sem);//V()

释放信号量,表示资源使用完毕,可以归还资源了。将信号量值加1。

基于环形队列的生产消费模型

在这里插入图片描述

空间和数据资源

生产者关注的是空间资源,消费者关心的是数据资源

  • 只要环形队列中有空间,生产者就可以进行生产
  • 而环形队列中有数据,消费者就可以消费数据

我们不防设空间资源为block_sem,数据资源为data_sem,那么它们的初始值怎么设置呢?

现在是用信号量来描述队列中的空间和数据资源,刚开始队列中是没有数据的,所以给block_sem的初始值设为队列的空间,data_sem的初始值是0,因为刚开始队列为空没有数据的。

生产者和消费者申请、释放信号量

生产者申请空间资源,释放数据资源

生产者的操作步骤:

1.如果block_sem不为0,说明队列中有空间资源,生产者申请信号量成功,那么对应的操作就是P(block_sem),V(data_sem)。此时队列中多了1块空间,那么data_sem就要–,也就是V(data_sem)
2.如果block_sem为0,那么生产者申请信号量失败,此时生产者就要挂起等待,等待有新的空间资源

消费者申请数据资源,释放空间资源

消费者的操作步骤和生产者基本一样
1.消费者申请data_sem,若data_sem不为0,消费者申请信号量成功,对应的操作时P(data_sem),那么V(block_sem),释放的就是空间资源,因为数据占了相应的空间
2.若data_sem,消费者申请信号量失败,消费者挂起等待,等待新的数据资源。

生产者和消费者要遵守的规则

1.快的不能把慢的套1个圈
2.慢的不能超过快的
在这里插入图片描述

  • 若生产者的速度比消费者的快,当生产者把队列生产满了,并再次遇到消费者。此时生产者再继续往前生产,那么再生产的数据就会覆盖掉,此时生产者就要挂起等待
  • 同样的道理,消费者的速度快,当消费者把数据都消费完了再进行消费就会消费到垃圾数据,此时应该挂起等待生产者继续生产数据

模拟实现基于环形队列的生产者消费者模型

我们用STL中的vector来模拟环形队列,分为RunQueue.hpp和mian.cc
相关说明:

RunQueue.hpp

  • 我们需要2个下标来标识生产者和消费者的位置,需要生产者消费者申请的资源,block_sem和data_sem
  • 我们要提供2个接口,分别是入队列和出队列,生产者的P操作,P(block_sem),V(data_sem),消费者的P(data_sem),V(data_sem)
  • 每当生产者生产1个空间,对应的生产者下标++,还要模上队列的空间,以防越界,消费者也是相同的操作

当2个下标指向相同的位置时,要么是刚开始为空,要么是其中1个速度快。当为空时,一定有1个申请信号量失败,同样当1个要超过令个是再申请信号量也会失败。因为信号量的本身就是1个计数器。

  1 #pragma once 2 3 #include<iostream>4 #include<pthread.h>5 #include<semaphore.h>6 #include<vector>7 #include<unistd.h>8 class Task9 {10   public:11     int x;12     int y;13   public:14     Task(int _x = 1,int _y = 10)15 16       :x(_x)17       ,y(_y)18     {}19     int run()                                                                                                                       20     {21       return x+y;22     }23     ~Task(){}24 };25 template<class T>26 class RunQueue27 {28   private:29     std::vector<T> v;30     int c_index;31     int p_index;32     int cap;33     sem_t block_sem;34     sem_t data_sem;35   public:36     RunQueue(int _cap = 6)37       :cap(_cap)38       ,c_index(0)39       ,p_index(0)40     {41       sem_init(&block_sem,0,cap);42       sem_init(&data_sem,0,0);43       v.resize(10);44     }45    void Push( T& data)46    {47      sem_wait(&block_sem);48      v[p_index] = data;49      p_index++;50      p_index %= cap;51      sem_post(&data_sem);52    }53    void Pop(T& data)54    {55       sem_wait(&data_sem);56       data = v[c_index];57       c_index++;58       c_index %= cap;59       sem_post(&block_sem);60    }61    ~RunQueue()62    {63      sem_destroy(&block_sem);64      sem_destroy(&data_sem);65    }66 67                                                                                                                                     68 };

main.cc

  • 主函数的话,创建2个线程,生产者生产数据,消费者消费数据即可
    1 #include"RunQueue.hpp"2 void* Consumer(void* arg)3 {4    //RunQueue<int>* rq = (RunQueue<int>*)arg;5    RunQueue<Task>* rq = (RunQueue<Task>*)arg;6    while(true)7    {8      int x,y;9      Task t;10      rq->Pop(t);11      std::cout<<"Consumer done:"<<t.x<<"+"<<t.y<<"="<<t.run()<<std::endl;                                                         12      sleep(1);13    }14 }15 void* Product(void* arg)16 {17    //RunQueue<int>* rq = (RunQueue<int>*)arg;18    RunQueue<Task>* rq = (RunQueue<Task>*)arg;19    while(true)20    {21      int x = rand() % 10 + 1;22      int y = rand() % 100 + 1;23      Task t(x,y);24      rq->Push(t);25      std::cout<<"Product done:"<<x<<"+"<<y<<"=?"<<std::endl;26    }27 }28 int main()29 {30   RunQueue<int> *rq = new RunQueue<int>;31 32   pthread_t c,p;33   pthread_create(&c,nullptr,Consumer,rq);34   pthread_create(&c,nullptr,Product,rq);35 36   pthread_join(c,nullptr);37   pthread_join(p,nullptr);38   delete rq;39   return 0;40 }   

先让消费者慢,生产者一下就把任务生产完,消费者开始做任务

在这里插入图片描述

这次让消费者快,生产者慢

在这里插入图片描述
刚开始没有数据,消费者刚进入申请数据资源失败挂起等待,生产者申请空间成功,生产者生产一个任务,消费者消费一个任务。
如果想要是多生产者和多消费者参考我上一篇生产者和消费者即可,这里博主就不加了。


http://chatgpt.dhexx.cn/article/MKeUE5VG.shtml

相关文章

Linux信号量

文章目录 POSIX信号量信号量的原理信号量的概念信号量函数 二元信号量模拟实现互斥功能基于环形队列的生产消费模型空间资源和数据资源生产者和消费者申请和释放资源必须遵守的两个规则代码实现信号量保护环形队列的原理 POSIX信号量 信号量的原理 我们将可能会被多个执行流同…

Linux —— 信号量

目录 一、POSIX信号量 1. 什么是信号量 2. 信号量的基本原理 二、与信号量相关的操作 1. 初始化信号量 2. 销毁信号量 3. 等待信号量 4. 发布信号量 三、基于环形队列的生产者消费者模型 1. 空间资源和数据资源 2. 生产者和消费者申请和释放资源 四、模拟实现基于…

Double取值intValue()与doubleValue()之参数缺省

Double调用intValue()是四舍五入向下取整。 调用doubleValue()才是取double真实值。

java.lang.NullPointerException: Attempt to invoke virtual method ‘int java.lang.Integer.intValue()‘

问题 对于PreparedStatement 对象设置参数时&#xff0c; 提示该错误; java.lang.NullPointerException: Attempt to invoke virtual method ‘int java.lang.Integer.intValue()’ 具体问题 2022-09-06 21:28:10.695 11368-11755/com.example.electronicmall E/AndroidRunt…

IntValue()方法 和 ValueOf()方法

intValue() 1.intValue()是java.lang.Number类的方法&#xff0c;Number是一个抽象类。Java中所有的数值类都继承它。也就是说&#xff0c;不单是Integer有intValue方法&#xff0c;Double&#xff0c;Long等都有此方法。 2.此方法的意思是&#xff1a;输出int数据。每个数值类…

Double取值intValue()与doubleValue()

描述一个之前没注意&#xff0c;手误造成的bug。 可以看出&#xff0c;Double调用intValue()结果类似于RoundingMode.DOWN。 调用doubleValue()才是取double真实值。

java中valueof_JAVA中intValue()和ValueOf()什么意思,还有Value什么意思

展开全部 intValue()和ValueOf()是数据类62616964757a686964616fe59b9ee7ad9431333366306538型转化的两个方法。 intValue() 如Integer类型&#xff0c;就会有intValue()方法&#xff0c;意思是说&#xff0c;把Integer类型转化为Int类型。 valueOf() 如String就有valueOf()方法…

IDEA告警:Unnecessary unboxing ‘xxx.intValue()‘

显式编码拆箱已包装的原始数值。在Java5及以上的版本&#xff0c;拆箱是不必要的&#xff0c;可以安全地删除。那么 JDK5 到底做了啥&#xff1f; 自动装箱&#xff08;auto-boxing&#xff09;与自动拆箱&#xff08;auto-unboxing&#xff09; Java语言的基本类型都有包装&…

latex自定义插入空行或者空格

空行有几种方法&#xff1a; 1.~\\ 2.\\[行距] 例如&#xff1a;\\[3pt] 最后&#xff0c;我的选择是&#xff1a; \vspace*{n\baselineskip}空格&#xff1a;

latex中加入空白行的一种方法

在两行文字中间加入“~\\”就可以达到空行的目的了 如图所示&#xff0c;上面是加入“~\\”&#xff0c;下图是得到的结果

latex句首缩进空格

有时候想要再句首加空格&#xff0c;但是会被自动忽略&#xff0c;于是可以使用命令 \hspace*{0.6cm} 例如&#xff1a; $initialize the initial solutions w,precision,max_iters,\\ while (w< precision) and (iters < max_iters):\\\hspace*{0.6cm} grad \gets \…

LaTeX数学公式中的空格

两个quad空格 a \qquad b 两个m的宽度 quad空格 a \quad b 一个m的宽度 大空格 a\ b 1/3m宽度 中等空格 a\;b 2/7m宽度 小空格 a\,b 1/6m宽度 没有空格 ab 紧贴 a\!b 缩进1/6m宽度 \quad、1em、em、m代表当前字体下接近字符‘M’的宽度&#xff08;approximately the …

Latex空格

Latex空格分许多种&#xff0c;分别有&#xff1a; 源代码 \documentclass[UTF8]{ctexart}\begin{document}\begin{tabular}{|l|l|l|l|} \hline 类别 & 用法 & 含义 & 效果\\ \hline 两个quad & a$\backslash$qquad b & 两个m的宽度 & a\qquad b \\ \h…

Latex 公式中空格添加

转自&#xff1a;https://blog.csdn.net/bennyfun79/article/details/15500575 两个quad空格a \qquad b两个m的宽度quad空格a \quad b一个m的宽度大空格a\ b1/3m宽度中等空格a\;b2/7m宽度小空格a\,b1/6m宽度没有空格ab 紧贴a\!b缩进1/6m宽度

latex中的对与错(对号√与叉号×)、空格

转载 LaTeX 对号和错号_Xovee的博客-CSDN博客_latex对号错号 【转载】LaTeX中的空格汇总_AXYZdong的博客-CSDN博客_latex空格符号怎么打出来 空格 对号√ 错号 代码 \documentclass{article} \usepackage[utf8]{inputenc} \usepackage{url} \usepackage{booktabs} \usepackag…

LaTex中输入空格以及换行

1. 使用\ 表示空格 以及调整空格的大小 quad空格a \qquad b两个m的宽度quad空格a \quad b一个m的宽度大空格a\ b1/3m宽度中等空格a\;b2/7m宽度小空格a\,b1/6m宽度没有空格ab 紧贴a\!b缩进1/6m宽度 \quad、1em、em、m代表当前字体下接近字符‘M’的宽度。 2.使用\\ 表示换行

latex公式中的空格如何表示?

两个quad空格 a \qquad b 两个m的宽度 quad空格 a \quad b 一个m的宽度 大空格 a\ b 1/3m宽度 中等空格 a\;b 2/7m宽度 小空格 a\,b 1/6m宽度 没有空格 ab 紧贴 a\!b 缩进1/6m宽度

Latex 中的空格汇总

讲讲 Latex 让人头疼的地方 为什么科学文献通常要求用 LaTeX \LaTeX LATE​X 书写呢&#xff1f;当然是因为好看呀。 LaTeX \LaTeX LATE​X 美观的排版背后是一个庞杂的符号和排版系统&#xff0c;光是看看就知道这根本不是一两天能全部掌握的&#xff0c;就连 LaTeX \LaTeX…