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

news2024/11/19 0:27:29

信号量概念

这篇文章是以前写的,里面讲了 System V的信号量的概念,POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。 但POSIX可以用于线程间同步。



信号量的概念

POSIX信号量的接口

初始化信号量

参数:
pshared:0表示线程间共享,非0表示进程间共享
value:信号量初始值,资源的初始值

销毁信号量 

等待信号量,P操作,表示要使用资源,将信号量值加1

发布信号量,V操作,表示资源使用完毕,可以归还资源了,将信号量值加1

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

生产消费者模型      

这篇文章里的生产者消费者模型,是基于一个queue展开了,也就是把queue当成一个整体来看,当成一个资源,我们对他进行加锁保护(锁就相当于二元信号量)。下面我们把这个生产者消费者模型改一下,是基于环形队列的,不把环形队列看成一个整体,把环形队列里的每个块看成很多个资源,这时候线程并发访问时会产生问题,就需要用信号量来解决

环形队列采用数组模拟,用模运算来模拟环状特性,生产者Prpducter向队列里生产任务,消费者Consumer向队列里拿数据

怎么判空和满?

当P,V指向同一个位置的时候,就是空或者满

怎么保证它们不访问同一个位置? 用信号量来管理

 

生产者关注的是空间资源,消费者关注的是数据资源,这两种资源可以分别用两种信号量来管理 ,假如开始的时候,空间资源是队列的大小,数据资源为0,此时生产者可以申请空间资源信号量成功,他就可以生产,但是消费者申请数据资源信号量时就失败等待,用两个信号量来管理者两个资源就可以很好的让生产者消费者同时访问同一个队列,但是不会访问到同一个位置,就可以很好的解决上面的问题

实现代码

大家可以把他们复制到VS Code下来看

main.cc

#include<iostream>
#include<ctime>
#include<unistd.h>
#include"RingQueue.hpp"
#include"task.hpp"

using namespace std;
struct ThreadData
{
    RingQueue<Task>* rq;
    string threadname;
};

void* Consumer(void* args)
{
    ThreadData* data=static_cast<ThreadData*>(args);
    RingQueue<Task>* rq=data->rq;
    string name=data->threadname;
    while(true)
    {
        //消费任务
        Task t;
        rq->Pop(&t);

        //处理任务
        t.run();
        printf("消费者得到一个任务:%s,who:%s,结果:%s\n",t.GetTask().c_str(),name.c_str(),t.GetResult().c_str());
        //用printf输出比较好,不会错乱,cout打印有时会错乱
        // cout<<"消费者得到一个任务:"<<t.GetTask()<<",who: "<<name<<",结果为:"<<t.GetResult()<<endl;
        // sleep(1);

    }
    return nullptr;
}
void* Producter(void* args)
{
    ThreadData* data=static_cast<ThreadData*>(args);
    RingQueue<Task>* rq=data->rq;
    string name=data->threadname;
    while(true)
    {
        //获取数据
        int data1=rand()%10+1;
        int data2=rand()%5;
        char op=opers[rand()%opers.size()];
        Task t(data1,data2,op);

        //生产任务
        rq->Push(t);
        printf("生产者生产一个任务:%s,who:%s\n",t.GetTask().c_str(),name.c_str());
        //用printf输出比较好,不会错乱,cout打印有时会错乱
        // cout<<"生产者生产一个任务:"<<t.GetTask()<<",who: "<<name<<endl;
        sleep(1);
    }
    return nullptr;
}

int main()
{
    srand(time(nullptr));
    RingQueue<Task>* rq=new RingQueue<Task>(10);

    pthread_t c[5], p[3];

    for (int i = 0; i < 3; i++)
    {
        ThreadData *td = new ThreadData();
        td->rq = rq;
        td->threadname = "Productor-" + std::to_string(i);

        pthread_create(p + i, nullptr, Producter, td);
    }
    for (int i = 0; i < 5; i++)
    {
        ThreadData *td = new ThreadData();
        td->rq = rq;
        td->threadname = "Consumer-" + std::to_string(i);

        pthread_create(c + i, nullptr, Consumer, td);
    }

    for (int i = 0; i < 3; i++)
    {
        pthread_join(p[i], nullptr);
    }
    for (int i = 0; i < 5; i++)
    {
        pthread_join(c[i], nullptr);
    }

    return 0;
}

RingQueue.hpp

#pragma once
#include<iostream>
#include<string>
#include<vector>
#include<pthread.h>
#include <semaphore.h>


using namespace std;

template<class T>
class RingQueue
{
public:
    void P(sem_t& sem)
    {
        sem_wait(&sem);
    }
    void V(sem_t& sem)
    {
        sem_post(&sem);
    }
    void Lock(pthread_mutex_t& mutex)
    {
        pthread_mutex_lock(&mutex);
    }
    void Unlock(pthread_mutex_t& mutex)
    {
        pthread_mutex_unlock(&mutex);
    }
public:
    RingQueue(int maxcap=5):maxcap_(maxcap),ringqueue_(maxcap),c_index_(0),p_index_(0)
    {
        sem_init(&cdata_sem_,0,0);
        sem_init(&pspace_sem_,0,maxcap);
        pthread_mutex_init(&c_mutex_,nullptr);
        pthread_mutex_init(&p_mutex_,nullptr);
    }

    void Push(const T& in)//生产
    {
        P(pspace_sem_);//生产者关注空间资源,申请空间资源,空间资源--

        Lock(p_mutex_);//下标也是共享资源,需要加锁保护
        ringqueue_[p_index_]=in;
        p_index_++;
        p_index_%=maxcap_;//维持环形特性
        Unlock(p_mutex_);

        V(cdata_sem_);//数据资源增多了,数据资源++

    }
    void Pop(T* out)//消费
    {
        P(cdata_sem_);//消费者关注数据资源,申请数据资源,数据资源--

        Lock(c_mutex_);//下标也是共享资源,需要加锁保护
        *out=ringqueue_[c_index_];
        c_index_++;
        c_index_%=maxcap_;//维持环形特性
        Unlock(c_mutex_);

        V(pspace_sem_);//消费了数据,空间就增多了
    }
    ~RingQueue()
    {
        sem_destroy(&cdata_sem_);
        sem_destroy(&pspace_sem_);
        pthread_mutex_destroy(&c_mutex_);
        pthread_mutex_destroy(&p_mutex_);
    }
  
private:
    vector<T> ringqueue_;//用数组模拟环形队列
    int maxcap_;//环形队列的最大容量

    int c_index_;//消费者的下标
    int p_index_;//生产者的下标

    sem_t cdata_sem_;//消费者关注的数据资源
    sem_t pspace_sem_;//生产者关注的空间资源

    pthread_mutex_t c_mutex_;
    pthread_mutex_t p_mutex_;
};

Task.hpp

#pragma once
#include <iostream>
#include <string>

using namespace std;
string opers="+-*/%";

enum
{
    Divzero = 1,
    Modzero,
    Unknown
};

class Task
{
public:
    Task()
    {}
    Task(int data1, int data2, char op) : _data1(data1), _data2(data2), _op(op), _result(0), _exitcode(0)
    {}
    void run()
    {
        switch (_op)
        {
        case '+':
        {
            _result = _data1+_data2;
            break;
        }
        case '-':
        {
            _result = _data1-_data2;
            break;
        }
        case '*':
        {
            _result = _data1*_data2;
            break;
        }
        case '/':
        {
            if (_data2 == 0) _exitcode = Divzero;
            else _result = _data1/_data2;
            break;
        }
        case '%':
        {
            if (_data2 == 0) _exitcode = Modzero;
            else _result = _data1%_data2;
            break;
        }
        default:
        {
            _exitcode=Unknown;
            break;
        }
        }
    }
    void operator()()
    {
        run();
    }
    string GetResult()
    {
        string r=to_string(_data1);
        r+=_op;
        r+=to_string(_data2);
        r+='=';
        r+=to_string(_result);
        r+='[';
        r+=to_string(_exitcode);
        r+=']';
        return r;
    }
    string GetTask()
    {
        string r=to_string(_data1);
        r+=_op;
        r+=to_string(_data2);
        r+="=?";
        return r;
    }

private:
    int _data1;
    int _data2;
    char _op;
    int _result;
    int _exitcode;
};

 

 

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

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

相关文章

Python pypattyrn库:简化设计模式的实现

更多Python学习内容&#xff1a;ipengtao.com 在软件开发中&#xff0c;设计模式是解决常见问题的最佳实践。设计模式提供了一种简洁、可复用的代码结构&#xff0c;能够提高代码的灵活性和可维护性。Python 语言作为一种灵活的编程语言&#xff0c;允许开发者使用多种设计模式…

认知杂谈83《同样读书:不同态度,别样收获》

内容摘要&#xff1a; 在知识的海洋中&#xff0c;读书的态度决定了收获的深度。主要有两种读书方式&#xff1a;一是期待书籍像快餐一样提供直接答案&#xff0c;结果常常一无所获&#xff1b;二是将书籍作为探索工具&#xff0c;认真思考&#xff0c;与作者进行深度的“对话”…

Java | Leetcode Java题解之第436题寻找右区间

题目&#xff1a; 题解&#xff1a; class Solution {public int[] findRightInterval(int[][] intervals) {int n intervals.length;int[][] startIntervals new int[n][2];int[][] endIntervals new int[n][2];for (int i 0; i < n; i) {startIntervals[i][0] inter…

多机部署,负载均衡-LoadBalance

文章目录 多机部署,负载均衡-LoadBalance1. 开启多个服务2. 什么是负载均衡负载均衡的实现客户端负载均衡 3. Spring Cloud LoadBalance快速上手使用Spring Cloud LoadBalance实现负载均衡修改IP,端口号为服务名称启动多个服务 负载均衡策略自定义负载均衡策略 LoadBalance原理…

面试系列-携程暑期实习一面

Java 基础 1、Java 中有哪些常见的数据结构&#xff1f; 图片来源于&#xff1a;JavaGuide Java集合框架图 Java 中常见的数据结构包含了 List、Set、Map、Queue&#xff0c;在回答的时候&#xff0c;只要把经常使用的数据结构给说出来即可&#xff0c;不需要全部记住 如下&…

Stable Diffusion绘画 | 插件-Addition Networks:单独控制LoRA

当 SD 使用到了进阶阶段&#xff0c;经常需要添加多个 LoRA 来生成图片&#xff0c;因此&#xff0c;提示词中难免会出现一系列的 LoRA 和相关触发词。 但很多时候&#xff0c;我们直接复制网上别人分享的完整提示词&#xff0c;会发现生成出来的效果不一样&#xff0c;这是怎么…

【嵌入式】嵌入式系统和图形用户界面(GUI)开发的图形库和框架

目录 1. **LVGL (Light and Versatile Graphics Library)**2. **TouchGFX**3. **EmWin**4. **Qt for Embedded**5. **SDL (Simple DirectMedia Layer)**6. **Nano-X**7. **Cairo**8. **GTK**9. **Allegro**10. **Qt Quick (QML)**11. **GUIX**12. **FLTK (Fast, Light Toolkit)…

Solidity智能合约中的异常处理(error、require 和 assert)

Solidity 中的三种抛出异常方法&#xff1a;error、require 和 assert 在 Solidity 开发中&#xff0c;异常处理是确保智能合约安全性和正确性的关键步骤。Solidity 提供了三种主要方法来抛出异常&#xff1a;error、require 和 assert。本文将详细介绍这三种方法的用途、实现方…

心觉:如何重塑高效学习的潜意识(2)明白你为什么这么学,才能学得更好

Hi&#xff0c;我是心觉&#xff0c;与你一起玩转潜意识、脑波音乐和吸引力法则&#xff0c;轻松掌控自己的人生&#xff01; 挑战每日一省写作181/1000天 上一篇文章我们讲了系统化学习和边学边用两种方法的优缺点 为什么有些人喜欢这种而不是那种 他们的底层心理逻辑不一…

智能识别猫猫

鸡蛋饼是什么猫 今天&#xff0c;有一位爱猫人士找到了我&#xff1a;“9月25日啦&#xff0c;炉石传说重新开服了&#xff01;” 我&#xff1a;“哦&#xff01;我知道这个&#xff0c;你是说&#xff0c;我现在该去领金卡了吗&#xff1f;” 爱猫人士&#xff1a;“不&am…

VMware下Ubuntu找不到共享文件夹

在VMware的设置中已经设置了共享文件夹&#xff0c;在Ubuntu系统中找不到&#xff0c;参考了网上其他的文章&#xff0c;发现还是不能解决问题&#xff0c;无意中尝试了一小步&#xff0c;没想到成功解决了&#xff0c;在这里记录一下。 1&#xff09;首先查询本机的gid 2&…

聚焦Llama新场景和AR眼镜,扎克伯格用AI赋能元宇宙,Meta Connect 2024开发者大会直播约起...

作者&#xff1a;十九 编辑&#xff1a;十九&#xff0c;李宝珠 北京时间 9 月 26 日凌晨 1 点&#xff0c;Meta Connect 2024 开发者大会即将举行&#xff0c;马克扎克伯格将聚焦 AI 和元宇宙&#xff0c;向大家分享 Llama 模型的更多潜在应用&#xff0c;并介绍 Meta 最新产品…

2024年汉字小达人区级自由报名备考冲刺:最新问题和官模题练一练

今天是2024年第十一届汉字小达人的区级自由报名活动的第二天。 我们继续回答几个关于汉字小达人的最新问题&#xff0c;做几道2024年官方模拟题&#xff0c;帮助孩子们少走弯路&#xff0c;再冲刺一般&#xff0c;更精准地备考2024年汉字小达人。 【温馨提示】本专题在比赛期…

芯科科技2024年Works With开发者大会登陆上海,物联网和人工智能的变革性融合带来无限精彩

谷歌、三星等生态大厂将带来重磅演讲和圆桌讨论&#xff0c;亦可切身体验多样化无线技术实作 中国&#xff0c;北京 – 2024年9月25日 – 安全、智能无线连接技术领域的全球领导厂商Silicon Labs&#xff08;亦称“芯科科技”&#xff0c;NASDAQ&#xff1a;SLAB&#xff09;&a…

MySQL InnoDB MVCC读写逻辑分析与调测

目标 1、构建MVCC读写场景 2、gdb调试MVCC过程&#xff0c;输出流程图&#xff08;函数级别调用过程&#xff09; 前提 准备1 打开服务端 查询mysqld进程号 线程树 打开客户端&#xff0c;想创建几个事务号就打开几个客户端 准备2 数据库mvcc&#xff0c;两个表test和stu…

JVM(HotSpot):虚拟机栈(JVM Stacks)之线程问题排查方法

文章目录 前言一、CPU占用过大二、程序运行很长时间没有结果三、总结 前言 本篇讲的排查都是基于Linux环境的。 一、CPU占用过大 这个一般是出现了死循环导致的。 1、先用top命令查看占用CPU的进程ID top2、再用ps命令查看对应的线程 就看一查看到对应的线程id ps H -eo …

王道考研视频——操作系统笔记

操作系统第一章&#xff01;入门 王道考研视频——操作系统笔记&#xff0c;第一部分&#xff0c;操作系统的概念和体系结构 0.0 课程白嫖指南_哔哩哔哩_bilibili0.0 课程白嫖指南是王道计算机考研 操作系统的第1集视频&#xff0c;该合集共计84集&#xff0c;视频收藏或关注UP…

openkylin介绍及其特点和优势

openKylin&#xff08;开放麒麟&#xff09;openKylin&#xff08;开放麒麟&#xff09; 社区是在开源、自愿、平等和协作的基础上&#xff0c;由基础软硬件企业、非营利性组织、社团组织、高等院校、科研机构和个人开发者共同创立的一个开源社区&#xff0c;致力于通过开源、开…

51单片机应用开发---keil 创建一个新工程并用Protues 8仿真(以点亮LED为例)

实现目标 1、掌握keil V5软件 创建一个新工程&#xff1b; 2、具体目标&#xff1a;1.会新建一个工程&#xff1b;2.编程实现点亮开发板的LED1. 一、新建工程步骤 1.1 在桌面上新建一个名字为 LED的文件夹 1.2 双击打开Keil uVision5 软件&#xff0c;点击 Project —>…

HTML和CSS中的浮动以及边框塌陷解决方案(内置练习及答案)

一、浮动概述 在HTML和CSS中&#xff0c;“浮动”&#xff08;Float&#xff09;是一种布局技术&#xff0c;它允许元素脱离其正常的文档流&#xff0c;向左或向右移动&#xff0c;直到它的外边缘碰到包含框或另一个浮动元素的边缘。浮动元素仍然保持块级盒模型的特性&#xff…