信号量

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

信号量原理

  • 之前我们知道被多个执行流同时访问的公共资源叫做临界资源,而临界资源不保护的话会造成数据不一性的问题。
  • 之前我们用互斥锁保护临界资源是把这个临界资源当做一个整体,只能让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 }

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

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


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

Linux-----信号量相关推荐

  1. Linux信号量 sem_t简介

    简介请移步: https://blog.csdn.net/qq_19923217/article/details/82902442 https://blog.csdn.net/evsqiezi/art ...

  2. linux申请信号量,linux 信号量

    https://www.jianshu.com/p/6e72ff770244 无名信号量 只适合用于一个进程的不同线程 #include #include #include #include #inc ...

  3. 最全面的 linux 信号量解析

    一.什么是信号量 信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有. 信号量的值为正的时候,说明它空闲.所测试的线程可以锁定而使用它.若为 0,说明它被占用,测试的线程 ...

  4. linux文件信号量删除,linux信号量_閑の洎茬

    1.1 创建信号量 int semget( key_t key,   //标识信号量的关键字,有三种方法:1.使用IPC--PRIVATE让系统产生, // 2.挑选一个随机数,3.使用ftok从文件 ...

  5. 最全面的linux信号量解析

    信号量 一.什么是信号量 信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程) 所拥有. 信号量的值为正的时候,说明它空闲.所测试的线程可以锁定而使用它.若为0,说明 它被占用, ...

  6. linux 信号量semget,51CTO博客-专业IT技术博客创作平台-技术成就梦想

    semget() 可以使用系统调用semget()创建一个新的信号量集,或者存取一个已经存在的信号量集: 系统调用:semget(); 原型:intsemget(key_t key,int nsems ...

  7. linux 信号量锁 内核,Linux内核中锁机制之信号量、读写信号量

    在上一篇博文中笔者分析了关于内存屏障.读写自旋锁以及顺序锁的相关内容,本篇博文将着重讨论有关信号量.读写信号量的内容. 六.信号量 关于信号量的内容,实际上它是与自旋锁类似的概念,只有得到信号量的进程 ...

  8. Linux信号量之用户态信号量(Posix信号量->无名信号量)

    相关API: 1.初始化信号量 int sem_init(sem_t* sem,int pshared,unsigned int value); //pshared为信号量最多由几个进程共享.Linu ...

  9. Linux信号量之内核信号量

    一.内核信号量 Linux内核的信号量在概念和原理上与用户态的System V的IPC机制信号量是一样的,但是它绝不可能在内核之外使用,它是一种睡眠锁. 如果有一个任务想要获得已经被占用的信号量时,信 ...

  10. linux信号量简介

    一.什么是信号量 为了防止多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种访问机制,它可以通过生成并使用令牌来授权,在同一时刻只能有一个线程访问代码的临界区域. 临界区域是指执行数据更新的 ...

最新文章

  1. Flutter开发之认识Flutter(一)
  2. 森林正版服务器,The Forest 专用服务器设置向导
  3. halcon的算子清点:Chapter 10 3d匹配
  4. 【杂谈】一招,同时可视化18个开源框架的网络模型结构和权重
  5. GCC the GNU
  6. 牛客练习-哈尔滨理工大学21级新生程序设计竞赛(同步赛)
  7. c语言从入门到精通 PPT,C语言从入门到精通第1章讲述.ppt
  8. ABAP 如何解析 JSON 数据试读版
  9. Labview OCR数字识别
  10. 微信小程序轮播图(详细)
  11. 解决局域网文件传输慢的问题
  12. 打开心扉计算机谱子,教你演唱咏叹调《人们叫我咪咪》(附曲谱)丨选自普契尼歌剧《艺术家的生涯》...
  13. XSS修炼之独孤九剑——笔记
  14. 微信小程序毕业设计 基于微信会议室预约小程序系统开题报告
  15. AI“头雁”百度的进取之道:善弈者通盘无妙手
  16. (完整项目源码)GPS定位源码整套管理系统,轨迹播放,车辆管理,电子围栏,报警记录,数据库/人员定位/宠物定位/物流跟踪/资产定位
  17. 硬盘安装Debian与Xp双系统
  18. 微凉大大,教你一步一步在linux中正确的安装Xcache加速php。
  19. 基于51单片机煤气天然气有毒气体检测系统蓝牙手机通信proteus仿真原理图PCB
  20. npm发布第三方插件

热门文章

  1. [编辑器]_从 VI 编辑器谈 FreeBSD 的前世今生以及编辑器本质
  2. 零基础入门机器学习——声音识别——打卡Task1
  3. 乌班图系统设置系统语言,以及中文输入法
  4. 广州空气质量数据分析
  5. 湖南大学 离散数学 2018年期末考试 参考答案
  6. vivo android 刷机教程,vivo刷机步骤盘点【图文教程】
  7. 上海计算机一级选择题库,上海计算机一级选择题
  8. 零基础自学Java要多久,是不是很难?
  9. PG数据库插件扩展搭建(一)
  10. 有限差分法解NS方程原理