Linux——信号量

目录

POSIX信号量

信号量原理

信号量概念

信号量函数

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

生产者和消费者申请和释放资源

单生产者单消费者

多生产者多消费者

多生产者多消费者的意义

信号量的意义


POSIX信号量

信号量原理

  1. 如果仅用一个互斥锁对临界资源进行保护,相当于把这块临界资源看作一个整体,同一时间只允许一个执行流对这块临界资源进行访问
  2. 如果将这块临界资源再分成多个区域,当多个执行流需要访问临界资源时,如果这些执行流访问的是临界资源的不同区域,那就可以继续并发访问,访问同一个资源的时候再进行同步和互斥

信号量概念

        信号量本质上就是一个计数器,记录的是临界资源中资源数目的计数器,它可以更细粒度的对临界资源进行管理。

        访问临界资源的时候,先申请信号量信号量的值--,这叫做预定资源,我们叫做P操作;使用完后,信号量的值++,这叫做释放资源,我们叫做V操作

        当我们申请了一个信号量,可以保证当前执行流一定具有一个资源,要说是哪一个那就需要我们自己编写代码决定了。

信号量函数

作用:初始化信号量

参数:

  • sem:需要初始化的信号量
  • pshared:传入0表示线程间共享,传入非0表示进程间共享
  • value:信号量的初始值(类似一个计数器)

返回值:成功返回0,失败返回-1,错误码被设置。

 

作用:销毁信号量

参数:要销毁的信号量

返回值:成功返回0,失败返回-1,错误码被设置。

 

作用:等待信号量

参数:要等待的信号量

返回值:

  • 等待信号量成功返回0,信号量的值减一。
  • 等待信号量失败返回-1,信号量的值保持不变。 

 

作用:释放信号量

参数:需要释放的信号量

返回值:

  • 发布信号量成功返回0,信号量的值加一。
  • 发布信号量失败返回-1,信号量的值保持不变。

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

        生产者最看重的就是队列中的空间,而消费者最看重的就是队列中的数据。只要有空间就可以生产数据,只要有数据就可以消费。

        用信号量来表示上述的数据:

  • spaceSem:表示有多少空间,开始设为N(队列长度)。
  • dataSem:表示有多少数据,开始设为0。

生产者和消费者申请和释放资源

        当生产数据的时候:

P(spaceSem) spaceSem--;
// 生产数据
V(dataSem)dataSem++;

        当消费数据的时候

P(dataSem)dataSem--;
// 消费数据
V(spaceSem)spaceSem++;

        当两个执行流同时访问的时候:

  • 如果消费者先执行,要P(申请)dataSem(数据),但是一开始的dataSem的值是0,所以就被阻塞了。
  • 如果生产者先运行,要P(申请)spaceSem(空间),一开始的spaceSem的值是N,就可以申请成功,生产数据,之后V(释放)dataSem(数据)。
  • 如果生产者把数据放满了,要P(申请)spaceSem(空间)就会失败,生产者就被阻塞。
  • 如果两个执行流都能获取想要的资源,那就可以实现并发访问。

单生产者单消费者

// Sem.hpp
#include <iostream>
#include <pthread.h>
#include <semaphore.h>// 封装一下信号量
class Sem
{
public: Sem(int value){sem_init(&_sem, 0, value);}void P(){sem_wait(&_sem);}void V(){sem_post(&_sem);}~Sem(){sem_destroy(&_sem);}
private:sem_t _sem; 
};
// ringQueue.hpp
#include <iostream>
#include <vector>
#include <pthread.h>
#include <unistd.h>
#include "Sem.hpp"using namespace std;const int g_default_num = 5;template <class T>
class RingQueue
{
public:RingQueue(int default_num = g_default_num):_ring_queue(default_num),_num(default_num),_c_step(0),_p_step(0),_space_sem(default_num),_data_sem(0){}~RingQueue(){}// 生产者void push(const T& in){_space_sem.P();_ring_queue[_p_step++] = in;_p_step %= _num;_data_sem.V();}// 消费者void pop(T* out){_data_sem.P();*out = _ring_queue[_c_step++];_c_step %= _num;_space_sem.V();}void debug(){cout << "size: " << _num << "queue: " << _ring_queue.size() << endl;}private:vector<T> _ring_queue;size_t _num;int _c_step; // 消费者下标int _p_step; // 生产者下标Sem _space_sem; // 空间信号量Sem _data_sem; // 数据信号量
};
// testMain.cc#include "ringQueue.hpp"
#include <ctime>void* consumer(void* args)
{RingQueue<int>* rq = (RingQueue<int>*)args;while (true){int x;// 1.从环形队列中获取任务rq->pop(&x);// 2.进行处理cout << "消费:" << x << endl;// sleep(1);}
}void* productor(void* args)
{RingQueue<int>* rq = (RingQueue<int>*)args;while (true){// 1.构建数据或任务对象,数据可能从任何地方来,那一定会有时间消耗int x = rand() % 10 + 1;// 2.推送到环形队列rq->push(x);cout << "生产:" << x << endl;// sleep(1);}
}int main()
{srand((unsigned)time(nullptr) ^ getpid());RingQueue<int>* rq = new RingQueue<int>();pthread_t c, p;pthread_create(&c, nullptr, consumer, (void*)rq);pthread_create(&p, nullptr, productor, (void*)rq);pthread_join(c, nullptr);pthread_join(p, nullptr);return 0;
}

        与基于阻塞队列的生产者消费者模型不同的是,阻塞队列是一块临界资源,就会有互斥和同步的问题,生产者和消费者访问临界资源的时候就要加互斥锁来保护临界资源,用信号量实现没有使用互斥锁,我们把资源的数目规定好,通过管理这些资源的数量,就可以对每一块资源更细粒度的管理。
        关于环形队列的实现就不过多赘述了,就是控制下标加上模运算。生产者消费者只要访问的不是环形队列中的相同区域,他们两个基本就没有关系,所以可以实现并发访问。我们维护的只有生产者和消费者之间的互斥和同步关系

多生产者多消费者

        如果是多生产和多消费该怎么做呢?我们要知道的是相比于单生产单消费要多维护什么关系,其实就是生产和生产间、消费和消费间的这两种互斥关系

        如果只加一把锁,本来生产和消费可以有很大概率并发执行,现在又多了锁的竞争,就可能变成串行执行,一把不行,那就加两把。

        生产者之间的临界资源就是空间,消费者之间的临界资源就是数据。

        既然有了锁就可以保护临界资源了,那么我是先申请信号量还是先申请锁呢?假如先申请锁,锁申请成功了,再申请信号量,此时就可能有很多信号量还没有分配出去,前面我们也说过,这个信号量是一种预定机制,即便申请了信号量也没有使用资源,那为何不先申请信号量呢,所以一般都是先申请信号量再加锁。

 

const int g_default_num = 5;template <class T>
class RingQueue
{
public:RingQueue(int default_num = g_default_num):_ring_queue(default_num),_num(default_num),_c_step(0),_p_step(0),_space_sem(default_num),_data_sem(0){pthread_mutex_init(&_clock, nullptr);pthread_mutex_init(&_plock, nullptr);}~RingQueue(){pthread_mutex_destroy(&_clock);pthread_mutex_destroy(&_plock);}// 生产者void push(const T& in){_space_sem.P();pthread_mutex_lock(&_plock);_ring_queue[_p_step++] = in;_p_step %= _num;pthread_mutex_unlock(&_plock);_data_sem.V();}// 消费者void pop(T* out){_data_sem.P();pthread_mutex_lock(&_clock);*out = _ring_queue[_c_step++];_c_step %= _num;pthread_mutex_unlock(&_clock);_space_sem.V();}void debug(){cout << "size: " << _num << "queue: " << _ring_queue.size() << endl;}private:vector<T> _ring_queue;size_t _num;int _c_step;            // 消费者下标int _p_step;            // 生产者下标Sem _space_sem;         // 空间信号量Sem _data_sem;          // 数据信号量pthread_mutex_t _clock; // 消费者之间的锁pthread_mutex_t _plock; // 生产者之间的锁
};

多生产者多消费者的意义

        其实生产者往容器缓冲区中放数据和消费者从容器缓冲区中拿数据,就是一个生产者在放,一个消费者在拿,那它的意义在哪呢?

        我们要思考的是,我们从哪里拿到的任务也就是生产任务前,我们拿到任务后该怎么做,如果只有一个执行流,它既要做这个也要做那个,中间还得加锁,那任务就是一个一个做的,如果使用多线程,那么多个线程就可以并发的处理这些动作。

信号量的意义

        信号量的意义是什么呢?

        看到这里是一定会带有问题的,阻塞队列时,我们要先申请锁,再检测,不成功就阻塞,唤醒后在检测,成功后再执行,但是使用信号量都没有检测,甚至可能都没有加锁。

        其实阻塞队列中我们并不清楚临界资源的情况,但信号量是一个计数器,它可以预定某种资源,在PV操作中我们也可以知道临界资源的情况。

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

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

相关文章

path模块

一、path模块作用 path模块提供了操作路径的功能 二、语法 (1) path.resolve 拼接规范的绝对路径 常用 (2) path.sep 获取操作系统的路径分隔符 (3) path.parse 解析路径并且返回对象 (4) path.basename 获取路径的基本名称 (5) path.dirname 获取路径的目录名 …

Android NDK入门:在应用中加入C和C++的力量

目录 ​编辑 引 NDK的设计目的 与Java/Kotlin的结合 使用场景 开发流程 设置项目以支持NDK 编写本地代码 使用JNI连接本地代码和Java/Kotlin代码 编译和运行你的应用 附 引 自诩方向是android方向的移动端开发工程师&#xff0c;却从来没有真正仔细了解过NDK&#…

【Unity】Tag、Layer、LayerMask

文章目录 层&#xff08;Layer&#xff09;什么是LayerLayer的应用场景Layer层的配置&#xff08;Tags & Layers&#xff09;Layer的数据结构LayerMaskLayer的选中和忽略Layer的管理&#xff08;架构思路&#xff09;层碰撞矩阵设置&#xff08;Layer Collision Matrix&…

Could not transform the global plan to the frame of the controller

报错&#xff1a; [ERROR] [1710509295.679888409, 296.695000000]: Extrapolation Error: Lookup would require extrapolation 0.003000000s into the future. Requested time 295.747000000 but the latest data is at time 295.744000000, when looking up transform from…

智能视频生产平台解决方案介绍

视频内容已经成为企业宣传、营销、培训等多维度沟通的重要媒介&#xff0c;美摄科技凭借其在智能视频生产领域的深厚积累和创新能力&#xff0c;推出了面向企业的智能视频生产平台解决方案&#xff0c;为企业提供多端多场景的智能化视频生产工具&#xff0c;助力企业轻松打造高…

C语言进阶:顺序表(数据结构基础) (以通讯录项目为代码练习)

和黛玉学编程呀 C语言基础知识也完成了&#xff0c;所以就来谈谈数据结构吧&#xff0c;这篇主要是为了连接数据结构和C语言 很高兴又和大家见面啦&#xff0c;这节我们就讲顺序表&#xff0c;一起加油> 目录 数据结构相关概念 顺序表&#xff08;线性表&#xff0c;顺序…

【研发日记】Matlab/Simulink技能解锁(一)——在Simulink编辑窗口Debug

文章目录 前言 时间阈值断点 信号阈值断点 周期步进 Signal Value Lable Data Inspector 分析和应用 总结 前言 近期在一些研发项目中使用Matlab/Simulink时&#xff0c;遇到了挺多费时费力的事情。所以利用晚上和周末时间&#xff0c;在这些方面深入研究了一下&#x…

大话设计模式——7.抽象工厂模式(Abstract Factory Pattern)

1.介绍 抽象工厂模式是工厂模式的进一步优化&#xff0c;提供一个创建一系列相关或相互依赖对象的接口&#xff0c;而无需指定它们具体的类。属于创建型模式。 UML图&#xff1a; 2.示例 车辆制造工厂&#xff0c;不仅可以制造轿车也可以用来生产自行车。 1&#xff09;Abs…

孪生网络(Siamese Network)和数字孪生的区别?

1. 孪生网络 孪生网络是一种特殊的网络框架&#xff0c;基于两个人工神经网络建立的耦合构架&#xff0c;通过比较两个样本在高维度空间的表征来判断它们的相似程度。它包含两个相同结构的神经网络&#xff0c;一个用于学习&#xff0c;一个用于验证学习效果。在训练过程中&am…

十、项目沟通管理

十、项目沟通管理 从马斯洛需求的各个层级上&#xff0c;都需要沟通的介入。如果缺乏沟通&#xff0c;甚至可能严重损伤身心健康。 沟通渠道 1、 规划沟通管理 ​ 规划沟通管理是基于每个相关方或相关方群体的信息需求、可用的组织资产&#xff0c;以及具体项目的需求&#x…

基于springboot+vue实现养老服务管理系统项目【项目源码+论文说明】计算机毕业设计

基于springbootvue实现养老服务管理系统演示 摘要 医疗水平和生活水平的不断提高造就了我们现在稳定、发展的社会&#xff0c;带来受益的同时也加重了人口老龄化程度。随着人口老龄化程度的不断加深&#xff0c;越来越多的社会资源在对养老方面注入。那么面对如此快速发展的养…

蜡烛图K线图采用PictureBox控件绘制是实现量化交易的第一步非python量化

用vb6.0开发的量化交易软件 VB6量化交易软件的演示视频演示如上 股票软件中的蜡烛图是非常重要的一个东西&#xff0c;这里用VB6.0自带的Picture1控件的Line方法就可以实现绘制。 关于PictureBox 中的line 用法 msdn 上的说明为如下所示 object.Line [Step] …