首页 > 其他分享 >【线程】POSIX信号量---基于环形队列的生产消费者模型

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

时间:2024-09-25 16:55:20浏览次数:3  
标签:信号量 string int --- mutex POSIX sem data2

信号量概念

这篇文章是以前写的,里面讲了 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;
};

 

 

标签:信号量,string,int,---,mutex,POSIX,sem,data2
From: https://blog.csdn.net/2302_81250321/article/details/142522899

相关文章

  • 新闻媒体宣发套餐扩大影响力和建立品牌形象方法-华媒舍
    在当今互联网时代,营销推广是任何企业必须要面对的挑战。而在众多的营销方式中,精准投放和新闻媒体宣发套餐推广成为了越来越受欢迎的选择。本文将从精准投放和新闻媒体宣发套餐推广两个方面进行科普介绍,解析其背后的重要意义和带来的百倍回报。1、精准投放精准投放,作为一种广......
  • mysql数据库 - anolisos安装
    文章目录一、anolisos系统介绍1.1、anolisos系统的起源1.2、anolisos系统的版本支持1.3、anolisos系统的特点1.4、anolisos系统的适用场景二、环境部署2.1、修改主机名2.2、修改静态ip地址2.3、关闭selinux2.4、关闭或放通防火墙端口三、安装mysql数据库3.1、更新yum源......
  • 基于微信小程序的小说实体书商城-计算机毕业设计源码+LW文档
    摘  要随着互联网技术和通讯技术的快速发展、成熟,两者最终结合到了一起,即移动互联网。移动互联网时代的到来,微信的普及,致使基于微信小程序的系统越来越多,因此,针对用户手机微信购买小说书籍方面的需求,特开发了本微信小程序小说实体书商城。本微信小程序小说实体书商城采用微信......
  • 如何部署北斗定位应用,基于国产自主架构LS2K1000LA-i处理器平台
    北斗卫星导航系统(以下简称北斗系统)是着眼于国内经济社会发展需要,自主建设、独立运行的卫星导航系统。经过多年发展,北斗系统已成为面向全球用户提供全天候、全天时、高精度定位、导航与授时服务的重要新型基础设施。图1北斗定位系统的应用优势强可控:北斗系统是国内自主研发的......
  • KBU1010-ASEMI单向整流桥KBU1010
    编辑:llKBU1010-ASEMI单向整流桥KBU1010型号:KBU1010品牌:ASEMI封装:KBU-4批号:2024+类型:单向整流桥电流(ID):10A电压(VF):1000V安装方式:直插式封装特性:大功率、整流扁桥产品引线数量:4产品内部芯片个数:4产品内部芯片尺寸:MIL工作结温:-55℃~150℃功率:中小功率包装方式:500/盒:3000/箱KBU1010应用......
  • java-重启异常断掉的线程和监控线程状态
    java-重启异常断掉的线程和监控线程状态背景主要代码打印结果总结背景有一个线程,可能会因为异常而终止掉。为了监控这个线程,我又启动一个线程去监控。主要代码代码主要是由两个线程和两个可以产生异常的方法,内容跟简单,如下importjava.time.LocalDateTime;impor......
  • MISC - 第五天( RouterPassView 路由器密码修复工具,steghide文件隐藏工具,多压缩包伪
    前言各位师傅大家好,我是qmx_07,今天继续讲解MISC相关知识点荷兰宽带数据泄露下载附件,是一个bin后缀的宽带数据文件,使用RouterPassView工具查看现代路由器都会让用户备份一个配置文件,以便重置恢复数据一般配置文件会包含账户名和密码RouterPassView介绍:用于恢复路......
  • Leetcode 626-换座位题目解析
    1.题目编写解决方案来交换每两个连续的学生的座位号。如果学生的数量是奇数,则最后一个学生的id不交换。按 id 升序 返回结果表。 2.数据准备CreatetableIfNotExistsSeat(idint,studentvarchar(255));TruncatetableSeat;insertintoSeat(id,student)v......
  • linux semaphore信号量操作
    信号量(semaphore)是操作系统中最常见的同步原语之一。spinlock是实现忙等待锁,而信号量则允许进程进入睡眠状态。下面将分析信号量的获取是释放操作。1、数据结构数据结构定义和初始化如下:include/linux/semaphore.h/*Pleasedon'taccessanymembersofthisstruc......
  • Java面试-Redis篇(二)
    分布式锁抢劵场景分布式锁使用集群分布式锁使用代码展示setnx实现分布式锁Redis实现分布式锁主要利用Redis的setnx命令。setnx是SETifnotexists(如果不存在,则SET)的简写。redisson给锁续期代码实现publicvoidredisLock()throwsInterruptedException......