【Linux】信号量与生产消费模型

news2024/12/23 10:59:04

我们已经实现过锁+条件变量的PC模型,
但是BlockingQueue并不能进行生产与消费的并发,原因在于我们使用的是STL提供的队列,进行了一个适配,底层的实现可能会修改到成员变量造成未知的错误。

而这次我们选择使用环形队列(底层vector),可以实现生产与消费的并发。

目录

  • POSIX信号量:
  • 生产消费模型:
    • 数据结构中的环形队列:
    • 理论:
    • 代码(单生产单消费):
    • 代码(多生产多消费):

POSIX信号量:

POSIX比System V版本的信号量更加简洁明了。
首先他们都是在在这里插入图片描述头文件下的

我们来简单的看一下接口:
关于信号量的一些详细概念可以看一次进程间通信。

信号量初始化:
在这里插入图片描述
第一个参数是你创建的sem对象地址,第二个参数由于我们是进程间的线程通信,设置为0即可,第三个是你信号量“计数器”个数。

关于第二个参数更详细的可以直接看参考文档。在这里插入图片描述
信号量销毁:
在这里插入图片描述
传入你的信号量对象地址即可。

P操作:
在这里插入图片描述
V操作:
在这里插入图片描述

生产消费模型:

数据结构中的环形队列:

环形队列在数据结构中有一个很讲究的点,那就是如何判断空与满。
在这里插入图片描述
当添加完一圈元素后,头与尾又指向了同一个位置
在这里插入图片描述
因此我们的解决方案通常是增加一个空节点进行判断或者增加一个计数。

但是利用信号量 + 环形队列实现PC即可解决这个判断问题,因为信号量本身就是一个计数器。

理论:

那我们该如何利用环形队列实现PC?

我们采用先单消费单生产进行,这样只需考虑生产者与消费者的关系。

在这里插入图片描述

代码(单生产单消费):

由于我们底层是使用vector模拟,因此在进行放数据或者拿数据时需要每次都进行%=一个N,防止超出vector范围。
RingQueue.hpp

#include <iostream>
#include <unistd.h>
#include <semaphore.h>
#include <pthread.h>
#include <vector>

const int defaultnum = 5;

template <class T>
class RingQueue
{
private:
    void P(sem_t &sem)
    {
        sem_wait(&sem);
    }
    void V(sem_t &sem)
    {
        sem_post(&sem);
    }
public:
    RingQueue(int cap = defaultnum) : _cap(cap), _p_index(0), _c_index(0), _ringqueue(cap)
    {
        sem_init(&_sem_data, 0, _cap);
        sem_init(&_sem_space, 0, _cap);
    }
    ~RingQueue()
    {
        sem_destroy(&_sem_data);
        sem_destroy(&_sem_space);
    }
    void Push(const T &in)
    {
        P(_sem_space);
        _ringqueue[_p_index++] = in;
        _p_index %= _cap;
        V(_sem_data);
    }
    void Pop(T* out)
    {
        P(_sem_data);
        *out = _ringqueue[_c_index++];;
        _p_index %= _cap;
        V(_sem_space);
    }

private:
    std::vector<T> _ringqueue;
    int _cap;

    int _p_index;
    int _c_index;

    sem_t _sem_data;
    sem_t _sem_space;
};

Main.cc

#include "RingQueue.hpp"
#include <ctime>

void *Consumer(void *args)
{
    while (true)
    {
        sleep(1);
        RingQueue<int> *rq = static_cast<RingQueue<int> *>(args);
        // 接收数据
        int data = 0;
        rq->Pop(&data);
        // 处理数据
        std::cout << "Consumer->" << data << std::endl;
    }
}
void *Producer(void *args)
{
    while (true)
    {
        RingQueue<int> *rq = static_cast<RingQueue<int> *>(args);
        // 生产数据
        int data = rand() % 10 + 1;
        rq->Push(data);
        std::cout << "Producer->" << data << std::endl;
    }
}
int main()
{
    srand(time(nullptr));
    RingQueue<int> rq;
    pthread_t p, c;
    pthread_create(&c, nullptr, Consumer, &rq);
    pthread_create(&p, nullptr, Producer, &rq);

    pthread_join(p, nullptr);
    pthread_join(c, nullptr);

    return 0;
}

注意,这里我们的数据依然可以传内置类型,自定义类型,或者可调用对象。

代码(多生产多消费):

多生产多消费势必带来生产与生产的关系,消费与消费的联系。

也就意味着我们要进行加锁保护,因为多个生产者线程同时访问vector与_c_index这些共享资源,因此需要加锁。

我们此时有两个加锁方案(在P之前,V之后加锁解锁或者P之后V之前)

    void Push(const T &in)
    {
        Lock(_p_mutex);
        P(_sem_space);
        _ringqueue[_p_index++] = in;
        _p_index %= _cap;
        V(_sem_data);
        Unlock(_p_mutex);
    }
    void Pop(T *out)
    {
        Lock(_c_mutex);
        P(_sem_data);
        *out = _ringqueue[_c_index++];
        _p_index %= _cap;
        V(_sem_space);
        Unlock(_c_mutex);
    }

我们怎么选择?
我们的PV操作本质是一种预定机制,放在加锁之前可以让所有的线程共同申请信号量,达到申请并发,所以我们选择P之后V之前加锁方案。

#include <iostream>
#include <unistd.h>
#include <semaphore.h>
#include <pthread.h>
#include <vector>
#include <mutex>

const int defaultnum = 5;

template <class T>
class RingQueue
{
private:
    void P(sem_t &sem)
    {
        sem_wait(&sem);
    }
    void V(sem_t &sem)
    {
        sem_post(&sem);
    }
    void Lock(pthread_mutex_t &mtx)
    {
        pthread_mutex_lock(&mtx);
    }
    void Unlock(pthread_mutex_t &mtx)
    {
        pthread_mutex_unlock(&mtx);
    }

public:
    RingQueue(int cap = defaultnum) : _cap(cap), _p_index(0), _c_index(0), _ringqueue(cap)
    {
        sem_init(&_sem_data, 0, _cap);
        sem_init(&_sem_space, 0, _cap);

        pthread_mutex_init(&_p_mutex, nullptr);
        pthread_mutex_init(&_c_mutex, nullptr);
    }
    ~RingQueue()
    {
        sem_destroy(&_sem_data);
        sem_destroy(&_sem_space);

        pthread_mutex_destroy(&_p_mutex);
        pthread_mutex_destroy(&_c_mutex);
    }
    void Push(const T &in)
    {
        P(_sem_space);
        Lock(_p_mutex);
        _ringqueue[_p_index++] = in;
        _p_index %= _cap;
        Unlock(_p_mutex);
        V(_sem_data);
    }
    void Pop(T *out)
    {
        P(_sem_data);
        Lock(_c_mutex);
        *out = _ringqueue[_c_index++];
        _p_index %= _cap;
        Unlock(_c_mutex);
        V(_sem_space);
    }
private:
    std::vector<T> _ringqueue;
    int _cap;

    int _p_index;
    int _c_index;

    sem_t _sem_data;
    sem_t _sem_space;
    pthread_mutex_t _p_mutex;
    pthread_mutex_t _c_mutex;
};

完~

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

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

相关文章

python实现盲反卷积算法

python实现盲反卷积算法 盲反卷积算法算法原理算法实现Python实现详细解释优缺点应用领域盲反卷积算法 盲反卷积算法是一种图像复原技术,用于在没有先验知识或仅有有限信息的情况下,估计模糊图像的原始清晰图像和点扩散函数(PSF)。盲反卷积在摄影、医学成像、天文学等领域…

监控Windows文件夹下面的文件(C#和C++实现)

最近在做虚拟打印机时&#xff0c;需要实时监控打印文件的到达&#xff0c;并移动文件到另外的位置。一开始我使用了线程&#xff0c;在线程里去检测新文件的到达。实际上Windows提供了一个文件监控接口函数ReadDIrectoryChangesW。这个函数可以对所有文件操作进行监控。 ReadD…

当 Nginx 出现请求的乱序到达,如何处理?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01; 文章目录 当 Nginx 出现请求的乱序到达&#xff0c;如何处理&#xff1f;一、理解请求乱序到达的现象二、请求乱序到达可能带来的影响三、解决方案&#xff08;一&#xf…

安卓嘀嗒清单v7.2.2.2高级版

软件介绍 TickTick是一款轻便高效的任务管理、日程管理&#xff08;GTD&#xff09;和时间管理应用&#xff0c;配备强大的记事和提醒功能。你可以在手机、平板、网页等多达11个平台上使用滴答清单记录大小事务、制定工作计划、整理购物清单、设置生日提醒&#xff0c;甚至安排…

CSS技巧专栏:一日一例 12 -纯CSS实现边框上下交错的按钮特效

CSS技巧专栏&#xff1a;一日一例 12 -纯CSS实现边框上下交错的按钮特效 大家好&#xff0c;今天我们来做一个上下边框交错闪动的按钮特效。 本例图片 案例分析 虽说这按钮给人的感觉就是上下两个边框交错变换了位置&#xff0c;但我们都知道border是没法移动的。那么这个按…

土耳其云手机提升TikTok电商效率

在数字化飞速发展的今天&#xff0c;TikTok不仅是一个社交平台&#xff0c;更是一个巨大的电商市场。随着TikTok电商功能在全球范围内的扩展&#xff0c;土耳其的商家和内容创作者正面临着前所未有的机遇。本文将详细介绍土耳其云手机怎样帮助商家抓住机遇&#xff0c;实现业务…

单片机学习历程

学习单片机的过程可以分为几个主要阶段&#xff0c;每个阶段都涉及不同的学习内容和技能提升。下面我将以一个典型的学习历程为例进行介绍&#xff1a; 初学阶段 1.入门理论学习&#xff1a; 开始接触单片机的基础知识&#xff0c;学习其工作原理、体系结构和常见的芯片类型…

怎样在 Nginx 中配置基于请求客户端 Wi-Fi 连接状态的访问控制?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01; 文章目录 怎样在 Nginx 中配置基于请求客户端 Wi-Fi 连接状态的访问控制一、理解请求客户端 Wi-Fi 连接状态二、Nginx 中的访问控制基础知识三、获取客户端 Wi-Fi 连接状态…

Qt 使用视口和窗口作图

物理坐标系与逻辑坐标系 绘图设备的物理坐标系是基本的坐标系&#xff0c;通过 QPainter 的平移、旋转等坐标变换可以得到更容 易操作的逻辑坐标系。 物理坐标系也称为视口&#xff08;viewport&#xff09;坐标系&#xff0c;逻辑坐标系也称为窗口&#xff08; window&…

《操作系统》(学习笔记)(王道)

一、计算机系统概述 1.1 操作系统的基本概念 1.1.1 操作系统的概念 操作系统&#xff08;OperatinggSystem&#xff0c;OS&#xff09;是指控制和管理整个计算机系统的硬件与软件资源&#xff0c;合理地组织、调度计算机的工作与资源的分配&#xff0c;进而为用户和其他软件…

[STM32]HAL库实现自己的BootLoader-BootLoader与OTA-STM32CUBEMX

目录 一、前言 二、BootLoader 三、BootLoader的实现 四、APP程序 五、效果展示 六、拓展 一、前言 听到BootLoader大家一定很熟悉&#xff0c;在很多常见的系统中都会存在BootLoader。本文将介绍BootLoader的含义和简易实现&#xff0c;建议大家学习前掌握些原理基础。 …

简单谈谈 手机版买卖现货白银的好和不好

随着科技的发展&#xff0c;现在买卖现货白银可以在移动设备上进行&#xff0c;那意味着投资者通过手机、平板电脑等都可以进行交易&#xff0c;这进一步解放了投资者交易的场所限制&#xff0c;让投资者有了更大的自由度。有条件的朋友&#xff0c;除了通过个人电脑做现货白银…

Java Selenium WebDriver:代理设置与图像捕获

在网络爬虫和自动化测试领域&#xff0c;Selenium WebDriver 是一个非常流行的工具&#xff0c;它允许开发者模拟用户在浏览器中的操作。然而&#xff0c;出于安全或隐私的考虑&#xff0c;有时我们需要通过代理服务器来发送请求。本文将介绍如何在Java环境中使用Selenium WebD…

系统架构设计师②:操作系统

系统架构设计师②&#xff1a;操作系统 操作系统作用 ①管理系统的硬件、软件、数据资源 ②控制程序运行 ③人机之间的接口 ④应用软件与硬件之间的接口 进程管理 进程是程序在一个数据集合上运行的过程&#xff0c;它是系统进行资源分配和调度的一个独立单位。它由程序块、…

Linux系统上安装zookeeper

百度网盘 通过网盘分享的文件&#xff1a;zookeeper_linux 链接: https://pan.baidu.com/s/1_hybXZVwTRkotz0VbwbSMw?pwd8888 提取码: 8888 1.将压缩包拖进虚拟机 2.解压压缩包 cd /ruanjian/zookeeper/ tar -zxvf apache-ZooKeeper-3.7.2-bin.tar.gz3. 进入到conf目录 cd …

【网络】网络编程套接字——UDP、TCP、UDP接口使用、TCP接口使用、UDP程序实例、TCP程序实例

文章目录 Linux网络1. UDP1.1 UDP接口使用1.1 UDP程序实例 2. TCP2.1 TCP接口使用2.2 TCP程序实例 Linux网络 1. UDP 在使用我们的UDP和TCP函数的时候&#xff0c;我们需要理解一些预备的知识&#xff1a; 源 IP 地址和目的 IP 地址&#xff1a; 在网络通信中&#xff0c;IP …

Chrome谷歌浏览器Console(控制台)显示文件名及行数

有没有这样的困扰&#xff1f;Chrome谷歌浏览器console(控制台)不显示编译文件名及行数? 设置&#xff08;Settings&#xff09;- > 忽略列表&#xff08;lgnore List&#xff09;-> 自定义排除规则&#xff08;Custom exclusion rules&#xff09; 将自定义排除规则…

Golang实现Word模板内容填充导出

这里我们使用一个广泛使用且免费处理 .docx 文件的库&#xff0c;github.com/nguyenthenguyen/docx. 安装 github.com/nguyenthenguyen/docx 库 首先&#xff0c;确保你已经安装了 docx 库&#xff1a; go get github.com/nguyenthenguyen/docx使用 docx 库处理 Word 模板 …

逆向案例二十九——某品威客登录,请求头参数加密,简单webpack

网址&#xff1a;登录- 一品威客网,创新型知识技能共享服务平台 抓到登陆包分析&#xff0c;发现请求头有参数加密&#xff0c;直接搜索 定位到加密位置&#xff0c;打上断点&#xff0c;很明显是对象f的a方法进行了加密。 往上找f&#xff0c;可以发现f被定义了&#xff0c;是…

Superset二次开发之筛选器native Filters 水平布局

引言 Apache Superset作为一个功能强大的开源数据探索和可视化平台&#xff0c;提供了丰富的配置选项来定制化用户体验。其中&#xff0c;HORIZONTAL_FILTER_BAR 是一个重要的配置项&#xff0c;专注于优化和改进Superset中的筛选器条布局与交互。 什么是HORIZONTAL_FILTER_B…