【Linux —— POSIX信号量 - 基于环形队列的生产消费模型】

Linux —— POSIX信号量 - 基于环形队列的生产消费模型

  • POSIX信号量
    • 信号量的概念
    • POSIX信号量的类型
    • 信号量的操作
  • POSIX信号量函数
  • 基于环形队列的生产消费模型
    • 设计思路
    • 同步和安全性
    • 代码

POSIX信号量

信号量的概念

 POSIX信号量是一种用于进程和线程之间同步的机制,主要用于控制对共享资源的访问。信号量是一种同步原语,通常表现为一个整数值,表示可用资源的数量。信号量的值不小于0。如果一个进程尝试将信号量的值减少到小于0,操作将被阻塞,知道信号量的值增加到允许的范围。

POSIX信号量的类型

POSIX信号量主要分为两种类型:

  1. 命名信号量:
    • 具有一个名称,可以通过该名称在不同的进程间访问。命名信号量通常用于进程间的同步。
    • 使用sem_open()函数创建或打开命名信号量
      2.未命名信号量:
    • 不具有名称,通常存在于内存中,适用于同一进程内的多个进程之间的同步。
    • 未命名信号量可以通过sem_init()函数进行初始化。

信号量的操作

信号量的基本操作包括:

  • P操作(等待操作):使用sem_wait()函数实现,尝试减少信号量的值。如果信号量的值为0,则调用线程将被阻塞,直到信号量的值大于0。
  • V操作(释放操作):使用sem_post()函数实现,增加信号量的值,通知其他等待的线程或进程信号量的可用性。

POSIX信号量函数

POSIX信号量提供了一组函数来创建、操作和销毁信号量。以下是一些常用的POSIX信号量函数及其参数和返回值:

  1. sem_init

功能: 初始化未命名信号量。
原型:

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

参数:

  • sem: 指向信号量对象的指针。
  • pshared: 如果为0,则信号量仅用于线程间同步;如果非0,则信号量可用于进程间同步。
    value: 信号量的初始值。

返回值:

  • 成功时返回0;失败时返回-1,并设置errno以指示错误类型。
  1. sem_destroy

功能: 销毁信号量。
原型:

int sem_destroy(sem_t *sem);

参数:

  • ·sem·: 指向要销毁的信号量对象的指针。
    返回值:
  • 成功时返回0;失败时返回-1,并设置errno。
  1. sem_wait

功能: 进行 P 操作(等待操作),尝试获取信号量。
原型:

int sem_wait(sem_t *sem);

参数:

  • sem: 指向信号量对象的指针。

返回值:

  • 成功时返回0;失败时返回-1,并设置errno。如果信号量的值为0,调用线程将被阻塞。
  1. sem_post
  • 功能: 进行V操作(释放操作),增加信号量的值。
  • 原型:
int sem_post(sem_t *sem);

参数:

  • sem: 指向信号量对象的指针。

返回值:

  • 成功时返回0;失败时返回-1,并设置errno。

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

下面通过STL的vector来设计一个环形队列,环形队列采用数组模拟,用模运算来模拟环状特性:
在这里插入图片描述
环形结构起始状态和结束状态都是一样的,不好判断为空或者为满,所以可以通过加计数器或者标记位来
判断满或者空。另外也可以预留一个空的位置,作为满的状态
asd

但是我们现在有信号量这个计数器,就很简单的进行多线程间的同步过程

环形队列实际上就是一个链表,只是在理解上理解为一个环形的:
在这里插入图片描述

设计思路

  1. 使用环形队列:
  • 环形队列是一种循环使用固定大小内存的数据结构,非常适合生产者-消费者模型。
  • 通过维护生产者指针(_p_step)和消费者指针(_c_step),可以实现对环形队列的环形访问
  1. 使用信号量:
  • 使用两个信号量:_data_sem(数据信号量)和_space_sem(空间信号量)。
  • _data_sem表示队列中可用数据的数量,初始值为0。
  • _space_sem表示队列中可用空间的数量,初始值为队列的最大容量(_max_cap)
  1. 使用互斥量:
  • 使用两个互斥量:_p_mutex(生产者锁)和_c_mutex(消费者锁)。
  • 互斥量用于保护生产者和消费者对队列的并发访问

同步和安全性

  1. 生产者:
  • 生产者首先使用 P (_space_sem) 申请可用空间,如果没有可用空间,会被阻塞
  • 获取_p_mutex锁,保护对队列的写入操作。
  • 将数据写入队列,并更新生产者指针_p_step
  • 释放_p_mutex锁。
  • 使用 V (_data_sem) 发布一个数据可用信号。
  1. 消费者:
  • 消费者首先使用 P (_data_sem) 申请可用数据,如果没有可用数据,会被阻塞。
  • 获取_c_mutex锁,保护对队列的读取操作。
  • 从队列中读取数据,并更新消费者指针_c_step
  • 释放_c_mutex锁。
  • 使用 V (_space_sem)发布一个空间可用信号。
  1. 同步和安全性:
  • 信号量确保了生产者和消费者对队列的访问是同步的。
  • 生产者在有可用空间时才能写入,消费者在有可用数据时才能读取。
  • 互斥量确保了生产者和消费者对队列的并发访问是安全的。
  • 每个线程在访问队列时都会获取相应的互斥量,保证了临界区的互斥访问。
  1. 其他考虑:
  • 在构造函数中初始化信号量和互斥量。
  • 在析构函数中销毁信号量和互斥量。
  • 确保在异常情况下,信号量和互斥量也能被正确销毁。

代码

  • RingQueue.hpp 环形队列的实现
#pragma once#include <iostream>
#include <vector>
#include <string>
#include <pthread.h>
#include <semaphore.h>template <typename T>
class RingQueue
{
private:void P(sem_t &s){sem_wait(&s);}void V(sem_t &s){sem_post(&s);}public:RingQueue(int max_cap) : _ringqueue(max_cap), _max_cap(max_cap), _c_step(0), _p_step(0){sem_init(&_data_sem, 0, 0);sem_init(&_space_sem, 0, max_cap);pthread_mutex_init(&_c_mutex, nullptr);pthread_mutex_init(&_p_mutex, nullptr);}void Push(const T &in) // 生产者{// 信号量本身就是一种资源预约机制,无需判断,即可知道资源的内部情况P(_space_sem);pthread_mutex_lock(&_p_mutex);_ringqueue[_p_step] = in;_p_step++;_p_step %= _max_cap;pthread_mutex_unlock(&_p_mutex);V(_data_sem);}void Pop(T *out) // 消费者{P(_data_sem);pthread_mutex_lock(&_c_mutex);*out = _ringqueue[_c_step];_c_step++;_c_step %= _max_cap;pthread_mutex_unlock(&_c_mutex);V(_space_sem);}~RingQueue(){sem_destroy(&_data_sem);sem_destroy(&_space_sem);pthread_mutex_destroy(&_c_mutex);pthread_mutex_destroy(&_p_mutex);}private:std::vector<T> _ringqueue;int _max_cap;int _c_step;int _p_step;sem_t _data_sem;  // 消费者   数据信号量sem_t _space_sem; // 生产者   空间信号量pthread_mutex_t _c_mutex;pthread_mutex_t _p_mutex;
};
  • Task.hpp Task类的实现
#pragma once
#include <iostream>class Task
{
public:Task(){}Task(int x , int y):_x(x),_y(y){}~Task(){}void Excute(){_result = _x + _y;}std::string Debug(){std::string msg = std::to_string(_x) + " + " + std::to_string(_y) + " = " + " ? ";return msg;}std::string Result(){std::string msg = std::to_string(_x) + " + " + std::to_string(_y) + " = " + std::to_string(_result);return msg;}void operator()(){Excute();}
private:int _x;int _y;int _result;
};
  • main.cc 代码上层的调用逻辑
#include "RingQueue.hpp"
#include "Task.hpp"
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <ctime>void *Consumer(void*args)
{RingQueue<Task> *rq = static_cast<RingQueue<Task> *>(args);while(true){Task t;// 1. 消费rq->Pop(&t);// 2. 处理数据t();std::cout << "Consumer-> " << t.Result() << std::endl;}
}
void *Productor(void*args)
{RingQueue<Task> *rq = static_cast<RingQueue<Task> *>(args);while(true){sleep(1);// 1. 构造数据int x = rand() % 10 + 1; //[1, 10]usleep(x*1000);int y = rand() % 10 + 1;Task t(x, y);// 2. 生产rq->Push(t);std::cout << "Productor -> " << t.Debug() << std::endl;}
}int main()
{srand(time(nullptr) ^ getpid());RingQueue<Task> *rq = new RingQueue<Task>(5);// 单单pthread_t c1, c2, p1, p2, p3;pthread_create(&c1, nullptr, Consumer, rq);pthread_create(&c2, nullptr, Consumer, rq);pthread_create(&p1, nullptr, Productor, rq);pthread_create(&p2, nullptr, Productor, rq);pthread_create(&p3, nullptr, Productor, rq);pthread_join(c1, nullptr);pthread_join(c2, nullptr);pthread_join(p1, nullptr);pthread_join(p2, nullptr);pthread_join(p3, nullptr);return 0;
}

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

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

相关文章

【netty系列-08】深入Netty组件底层原理和基本实现

Netty系列整体栏目 内容链接地址【一】深入理解网络通信基本原理和tcp/ip协议https://zhenghuisheng.blog.csdn.net/article/details/136359640【二】深入理解Socket本质和BIOhttps://zhenghuisheng.blog.csdn.net/article/details/136549478【三】深入理解NIO的基本原理和底层…

数据结构(邓俊辉)学习笔记】串 16——Karp-Rabin算法:串即是数

文章目录 1. 化串为数2. 凡物皆数3. 亦是数 1. 化串为数 接下来的这节&#xff0c;我们再来讨论一种十分另类的串匹配算法&#xff0c;也就是所谓的 Karp-Rabin 算法。回顾此前所介绍的几种串匹配算法&#xff0c;我们所面临的难题是一样的。也就是说在这里&#xff0c;我们每次…

Windows 10/11降级漏洞的工具包现已发布 仅供安全测试

早前有研究人员在分析 Windows 10/11 更新机制时发现微软虽然已经考虑到潜在的安全问题增加了各种限制&#xff0c;但还是存在失误因此存在弱点&#xff0c;研究人员则通过该弱点成功降级了系统。通过该漏洞不仅可以成功降级系统&#xff0c;同时系统还会认为自己已经完成更新并…

Java面试题·区别题·JavaSE部分

系列文章目录 总章 Java区别题 文章目录 系列文章目录前言private/默认/protected/public权限修饰符的区别&和&&区别和联系&#xff0c;I和II区别和联系if和switch的不同之处和equals的区别和联系数组做形参和可变参数做形参联系和区别接口和抽象类的异同之处面向…

嵌入式day36

数据库 专业存储数据、大量数据 数组、链表、变量---->内存&#xff1a;程序运行结束、掉电数据丢失 文件---->硬盘&#xff1a;程序运行结束、掉电数据不丢失 数据库---->硬盘 数据库文件与普通文件区别&#xff1a; 1.普通文件对数据管理&#xff08;增删改查…

Linux入门攻坚——30、sudo、vsftpd

su&#xff1a;Switch User&#xff0c;即切换用户 su [-l user] -c ‘COMMAND’ 如&#xff1a;su -l root -c ‘COMMAND’ 如果没有指定-l user&#xff0c;则默认是root sudo&#xff1a;可以让某个用户不需要拥有管理员的密码&#xff0c;而可以执行管理员的权限。 需…

基于RS232的VGA显示

前言 基于ROM的VGA显示缺点&#xff1a;需要将图片转化为mif文件&#xff0c;使用的RAM是FPGA内部RAM模拟出来的&#xff0c;占用资源大切换显示图片需要重新转化&#xff0c;对ROM进行写入&#xff0c;使用极不方便&#xff0c;因此这里采用RS232进行VGA显示。 正文 一、基于…

代码随想录Day 28|题目:122.买卖股票的最佳时机Ⅱ、55.跳跃游戏、45.跳跃游戏Ⅱ、1005.K次取反后最大化的数组和

提示&#xff1a;DDU&#xff0c;供自己复习使用。欢迎大家前来讨论~ 文章目录 题目题目一&#xff1a;122.买卖股票的最佳时机 II贪心算法&#xff1a;动态规划 题目二&#xff1a;55.跳跃游戏解题思路&#xff1a; 题目三&#xff1a; 45.跳跃游戏 II解题思路方法一方法二 题…

鸿蒙开发入门day15-焦点事件

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;还请三连支持一波哇ヾ(&#xff20;^∇^&#xff20;)ノ&#xff09; 目录 焦点事件 基础概念与规范 基础概念 走焦规范 走焦算法 获焦/失…

【逐行注释】MATLAB下的UKF(无迹卡尔曼滤波),带丰富的中文注释,可直接复制到MATLAB上运行,无需下载

文章目录 程序组成部分完整代码运行结果主要模块解读:运动模型绘图部分误差统计特性输出程序组成部分 由模型初始化、运动模型、UKF主体部分、绘图代码和输出部分组成: 完整代码 将下列代码复制粘贴到MATLAB里面,即可运行: % 三维状态量的UKF例程 % 作者联系方式:微信…

安全面试常见问题任意文件下载

《网安面试指南》http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247484339&idx1&sn356300f169de74e7a778b04bfbbbd0ab&chksmc0e47aeff793f3f9a5f7abcfa57695e8944e52bca2de2c7a3eb1aecb3c1e6b9cb6abe509d51f&scene21#wechat_redirect 1.1 任意文件下…

培训第三十九天(了解docker-compose,docker-compose编排容器,配置harbor服务)

一、回顾 1、拉取私有仓库镜像 # 配置dockerdocker pull 10.0.0.10:5000/centosnginx:v0 2、容器网络类型 brideg(net) default# docker启动之后会生成新的虚拟网卡&#xff0c;网卡的名称docker0# 网段默认是172.17.0.1# 所有的容器都桥接docker0&#xff0c;通过桥接共享网…

LRN正则化是什么?

LRN正则化&#xff0c;全称为Local Response Normalization&#xff08;局部响应归一化&#xff09;&#xff0c;是一种在深度学习&#xff0c;特别是在卷积神经网络&#xff08;CNN&#xff09;中常用的正则化技术。该技术旨在通过模拟生物视觉系统中的侧抑制现象&#xff0c;…

OpenLayers3, 设置地图背景

文章目录 一、前言二、代码实现三、总结 一、前言 本文基于OpenLayers3&#xff0c;实现地图加入背景图的功能。 二、代码实现 <!DOCTYPE html> <html xmlns"http://www.w3.org/1999/xhtml"> <head><meta http-equiv"Content-Type"…

QT学习ubuntu qt + desktop

环境搭建 ubuntu 安装QT 遇到kit 选择不了 通过sudo apt-get install qt5-default去安装SDK的时候报错&#xff1a; Package qt5-default is not available, but is referred to by another package. This may mean that the package is missing, has been obsoleted, or is …

Linux——nginx 负载均衡

常规的web服务器一般提供对于静态资源的访问&#xff0c;比如说&#xff1a;图片、web样式 网站提供的大部分交互功能都需要web编程语言的支持&#xff0c;而web服务对于程序的调用&#xff0c;不管编译型语言还是解释型语言&#xff0c;web服务同将对于应用程序的调用递交给通…

在蓝桥云课ROS中快速搭建Arduino开发环境

普通方式 一步步慢悠悠的搭建和讲解需要5-6分钟&#xff1a; 如何在蓝桥云课ROS中搭建Arduino开发环境 视频时间&#xff1a;6分40秒 高效方式 如何高效率在蓝桥云课ROS中搭建Arduino开发环境 视频时间&#xff1a;1分45秒 配置和上传程序到开发板 上传程序又称为下载程序h…

html+css+js网页设计 婚庆网站8个页面

htmlcssjs网页设计 婚庆网站8个页面 网页作品代码简单&#xff0c;可使用任意HTML编辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作&#xff09;。 获取源码 1&#xff…

C#骑砍逻辑类Mod制作详细解说

前言&#xff1a; 最近在研究骑砍的mod&#xff0c;主要是想修改其中的逻辑部分&#xff0c;因此有了这篇帖子。 一&#xff0c;文件夹与XML配置 在Modules创建一个新文件夹&#xff0c;文件夹名称随意&#xff0c;不影响实际的读取。 文件夹下面的位置需要固定&#xff0c;因…

闲置物品|基于SprinBoot+vue的校园闲置物品交易平台(源码+数据库+文档)

校园闲置物品交易平台 目录 基于SprinBootvue的校园闲置物品交易平台 一、前言 二、系统设计 三、系统功能设计 5.1系统功能实现 5.2管理员模块实现 5.3用户模块实现 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xf…