当前位置: 首页 > article >正文

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

信号量概念

这篇文章是以前写的,里面讲了 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.kler.cn/a/322588.html

相关文章:

  • Android Activity Manager Service (AMS) 深入详解及源码分析
  • Springboot 整合 Java DL4J 打造金融风险评估系统
  • 【MySQL】RedHat8安装mysql9.1
  • django从入门到实战(三)——CBV视图介绍
  • 2025年法定节假日日历
  • nodejs+mysql+vue3 应用实例剖析
  • windows10使用bat脚本安装前后端环境之msyql5.7安装配置并重置用户密码
  • Meta震撼发布Llama3.2大规模模型
  • 记录QTreeView使用(item勾选,事件,过滤)
  • cubemx配置ADC
  • [3]Opengl ES着色器
  • ST188单光束反射式红外光电传感器心率测量原理
  • 混拨动态IP代理的优势是什么
  • 网络编程(10)——json序列化
  • leetcode721. 账户合并
  • 高级算法设计与分析 学习笔记9 跳表
  • 【论文阅读】RISE: 3D Perception Makes Real-World Robot Imitation Simple and Effective
  • 动态规划入门题目->使用最小费用爬楼梯
  • Ceph 基本架构(一)
  • Docker实践与应用
  • QT开发模式(一):界面和业务逻辑分离
  • 基于Hive和Hadoop的电商消费分析系统
  • 人工智能之计算机视觉的发展历程与相关技术内容,相应的模型介绍
  • docker-文件复制(docker cp:用于在Docker主机和容器之间拷贝文件或目录)
  • Windows系统的Tomcat日志路径配置
  • 在 Ubuntu 上构建 Electron 项目时的问题记录和解决方法