基于信号量的生产者与消费者模型

信号量

本质:计数器 + 等待队列 + 向外提供的使执行流阻塞 / 唤醒的功能接口

实现同步的原理:进程获取临界资源之前,要先获取信号量资源;
实现互斥的原理:一个进程获取了该临界资源之后,另一个进程无法再访问该临界资源。(0/1计数器)

对资源进行计数,统计当前的资源数量,通过自身的计数,就可以进行条件判断,是否能够进行操作,若不能获取资源,则阻塞当前执行流。

在程序初始化阶段,根据实际资源数量初始化信号量计数器数值,在每次获取资源之前,先获取信号量(先去判断计数是否大于0,若大于0,则计数-1,直接返回,获取数据;否则阻塞当前执行流)
其它执行流生产一个资源后,先判断计数器是否 <0 ,若小于0,则唤醒一个执行流,然后进行计数 +1

接口介绍:

sem_t sem;

int sem_init(sem_t *sem, int pshared, int value); //初始化操作
//pshared:这个参数决定了当前的信号量用于进程间还是线程间:0-线程间 !0-进程间
//value:实际的资源数量,用于初始化信号量计数器初值

int sem_wait(sem_t *sem); //阻塞操作---若没有资源则直接阻塞
int sem_post(sem_t *sem); //唤醒操作
int sem_destroy(sem_t *sem); //销毁操作

代码实现:

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

#define QUEUE_MAX 5 
class RingQueue{
public:
    RingQueue(int maxq = QUEUE_MAX):_queue(maxq), _capacity(maxq), _step_read(0), _step_write(0){
        //sem_init(信号量, 进程/线程(0)标志, 信号量初值)
        sem_init(&_lock, 0, 1); //用于实现互斥锁
        sem_init(&_sem_data, 0, 0); //数据空间计数初始为0
        sem_init(&_sem_idle, 0, maxq); //空闲空间计数初始为数组容量
    }
    ~RingQueue(){
        sem_destroy(&_lock);
        sem_destroy(&_sem_data);
        sem_destroy(&_sem_idle);
    }
    bool push(int data){
        //1.判断是否能够访问资源,不能访问则阻塞
        sem_wait(&_sem_idle);//空闲空间计数的判断,空闲空间计数 -1
        //2.能访问,则加锁,保护访问过程
        sem_wait(&_lock);//lock计数不大于1,当前若可以访问则-1,别人就不能访问了
        //3.资源的访问
        _queue[_step_write] = data;
        _step_write = (_step_write + 1) % _capacity;//走到最后,从头开始
        //4.解锁
        sem_post(&_lock);//lock计数+1,唤醒其它因为加锁阻塞的线程
        //5.入队数据之后,数据空间计数+1,唤醒消费者
        sem_post(&_sem_data);
        return true;
    }
    bool pop(int *data){
        sem_wait(&_sem_data);//有没有数据
        sem_wait(&_lock);//有数据则加锁保护访问数据的过程
        *data = _queue[_step_read]; //获取数据
        _step_read = (_step_read + 1) % _capacity;
        sem_post(&_lock);//解锁操作
        sem_post(&_sem_idle);//取出数据,则空闲空间计数+1,唤醒生产者
        return true;
    }

private:
    std::vector<int> _queue; //数组  vector需要初始化节点数量
    int _capacity; //队列的容量
    int _step_read; //获取数据的位置下标
    int _step_write; //写入数据的位置下标

    sem_t _lock; //这个信号量用于实现互斥

    //这个信号量用于对空闲时间进行计数
    //---对于生产者来说空闲空间计数 >0 的时候才能写数据 --- 初始为节点个数
    sem_t _sem_idle;

    //这个信号用于对具有数据的空间进行计数
    //---对于消费者来说有数据的空间计数 >0 的时候才能取出数据 --- 初始为0
    sem_t _sem_data;
};

void *thr_productor(void *arg){
    //这个参数是我们的主线程传递过来的线程
    RingQueue *queue = (RingQueue*)arg; //类型强转
    int i = 0;
    while(1){
        //生产者不断生产数据
        queue->push(i);//通过Push接口操作queue中的成员变量
        printf("productor push data:%d\n", i++);
    }
    return NULL;
}

void *thr_customer(void *arg){
    RingQueue *queue = (RingQueue*)arg;
    while(1){
        //消费者不断获取数据进行处理
        int data;
        queue->pop(&data);
        printf("customer pop data:%d\n",data);
    }
    return NULL;
}

int main(){
    pthread_t ptid[4], ctid[4];
    int ret, i;
    RingQueue queue;
    for(i = 0; i < 4; i++){
        ret = pthread_create(&ptid[i], NULL, thr_productor, (void*)&queue);
        if(ret != 0){
            printf("creat productor thread error\n");
            return -1;
        }
        ret = pthread_create(&ctid[i], NULL, thr_customer, (void*)&queue);
        if(ret != 0){
            printf("creat customer thread error\n");
            return -1;
        }
    }

    for(i = 0; i < 4; i++){
        pthread_join(ptid[i], NULL);
        pthread_join(ctid[i], NULL);

    }
    return 0;
}

运行结果:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值