基于阻塞队列的生产消费模型

目录

1,前言介绍

2,生产消费模型

3,任务类

4,使用练习

5,结果

6,码云链接


1,前言介绍

         什么意思呢,就是当生产者往阻塞队列生产数据,候生当队列满了的时产者等待,消费者消费数据同时唤醒生产者生产数据,当消费者消费数据阻塞队列为空时,消费者等待,生产者生产数据之后唤醒消费者。这里的等待指的是在各自的条件变量上面等待,这样我们就实现了一种同步关系,及消费和生产按照一定的关系,不是想读独立的,这里的具体如何同步也由我们自己控制、设计。在加上我们上节的锁来保护这个阻塞队列临界资源。

2,生产消费模型

兄弟们,都在注释里面,非常详细了,就不浪费时间在一一描述每一个细节了,看注释哦。

#include 
#include 
#include 
#include 
using namespace std;

// 目标是设计一个基于阻塞队列的生产消费模型。

const uint32_t Defaultcap = 5;
template 
class BlockQueue
{

public:
    BlockQueue(uint32_t cap = Defaultcap)
        : cap_(Defaultcap)
    {
        // 初始化互斥量和条件变量。
        pthread_mutex_init(&lock_, nullptr);
        pthread_cond_init(&client_cond_, nullptr);
        pthread_cond_init(&ser_cond_, nullptr);
    }

    ~BlockQueue()
    {
        // 销毁资源。
        pthread_mutex_destroy(&lock_);
        pthread_cond_destroy(&client_cond_);
        pthread_cond_destroy(&ser_cond_);
    }

public:
    T push(const T &in)
    {
        // 1,访问临界资源=阻塞队列=加锁。
        lockqueue();

        // 2.为满,条件不满足,生产者不能生产,等待。
        while (isfull())
        {
            ser_lock_wait();
        }

        //3. 条件满足,可以生产。
        pushdata(in);
        /// 4,生产完成,解锁。
        unlockqueue();
        // 这里西安解锁再唤醒消费者消费,尽量不要再锁中做锁外也可以做的事情,太浪费资源了。
        // 5,唤醒消费者消费。
        awaken_cond_client();
    }

    T pop()
    {
        //1,同理 加锁
        lockqueue();

       //2,条件不满足,无法消费,等待。
        while(isempty())
        {
            cliend_lock_wait();
        }
        //3,条件满足,消费
        T tmp=popdata();
       
        //4,消费完成,立马解锁。
        unlockqueue();

        //5,唤醒生产者生产.
        awaken_cond_ser();

        //这里是必须要有的,忘记写了那就扯犊子了。
         return tmp;
    }




private:
    // 加锁
    void lockqueue()
    {
        pthread_mutex_lock(&lock_);
    }
    // 解锁
    void unlockqueue()
    {
        pthread_mutex_unlock(&lock_);
    }
    // 判断阻塞队列是否位空。
    bool isempty()
    {
        return queue_.empty();
    }
    // 判断阻塞队列是否为满
    bool isfull()
    {
        return queue_.size() == cap_;
    }
    // 让消费者再消费者的条件变量等待
    void cliend_lock_wait()
    {
        pthread_cond_wait(&client_cond_, &lock_);
    }
    // 让生产者再生产者的条件变量等待。
    void ser_lock_wait()
    {
        pthread_cond_wait(&ser_cond_, &lock_);
    }
    // 唤醒消费者消费。
    void awaken_cond_client()
    {
        pthread_cond_signal(&client_cond_);
    }
    // 唤醒生产者生产.
    void awaken_cond_ser()
    {
        pthread_cond_signal(&ser_cond_);
    }
    void pushdata(const T& in)
    {
        queue_.push(in);
    }
    T popdata()
    {
        T tmp=queue_.front();
        queue_.pop();
        return tmp;
    }

private:
    int cap_;                    // 容量
    queue queue_;             // 保存任务的阻塞队列。
    pthread_mutex_t lock_;       // 保护阻塞队列的互斥量,锁。
    pthread_cond_t client_cond_; // 消费者等待的条件变量。
    pthread_cond_t ser_cond_;    // 生产者等待的条件变量。
};

3,任务类

一个简单的+-*/%运算的任务类,并且他自己有处理任务的方法,同时,兄弟们,看注释哦。

#pragma once
#include 
#include 
// 一个任务类,并且他自己实现了处理任务的方法。
class Task
{
public:
    Task(int one = 1, int two = 1, char op = '*')
        : one_(one), two_(two), op_(op)
    {

    }
    // 析构不需要写。

    // 处理任务
    int run()
    {
        int resault = -1;
        switch (op_)
        {
        case '+':
            resault = one_ + two_;
            break;
        case '-':
            resault = one_ - two_;
            break;
        case '*':
            resault = one_ * two_;
            break;
        case '/':
            if (two_ == 0)
            {
                resault = -1;
                flag = false;
            }
            else
            {
                resault = one_ / two_;
            }
            break;
        case '%':
            if (two_ == 0)
            {
                resault = -1;
                flag = false;
            }
            else
            {
                resault = one_ % two_;
            }
            break;
        default:
            flag = false;
            resault = -1;
            break;
        }
        return resault;
    }

    // 用算符重载。
    int operator() ()
    {
        return run();
    }

    // 把需要处理的任务用输出型参数带回,方便打印。
    void getdata(int *one, int *two, char *op)
    {
        *one = one_;
        *two = two_;
        *op = op_;
    }
    bool istrue()
    {
        return flag;
    }

private:
    int one_;
    int two_;
    char op_;
    bool flag = true;
};

4,使用练习

搞了俩个线程,一个生产数据,一个消费数据,主要是看他的同步互斥关系。互斥关系由一把锁维护的哦,兄弟们,看注释,不多讲了。

#include "blockqueue.hpp"
#include "Task.hpp"
#include
string ops = "+-*/%";

void *consumer(void *args)
{
    BlockQueue *q = (BlockQueue *)args;

    while (true)
    {
        sleep(1);
        // 1,只管消费任务就完了。
        Task tmp = q->pop();
        // 2,处理任务。
        int resault = tmp();
        int one;
        int two;
        char op;
        // 带回是什么任务。
        tmp.getdata(&one, &two, &op);
        if (tmp.istrue())
            cout << "处理正常任务:" << one << op << two << "=" << resault << endl;
        else
            cout << "处理异常任务:" << one << op << two << "=" << resault << endl;
    }
}

void *productor(void *args)
{
    BlockQueue *q = (BlockQueue *)args;
    while (true)
    {
        sleep(1);
        // 1,只管生产任务就好了。
        int one_;
        int two_;
        char op_;
        one_=rand()%10;
        two_=rand()%10;
        op_=ops[rand()%ops.size()];
        Task tmp(one_, two_, op_);
        q->push(tmp);
        cout<<"productor生产了一个任务: "< dp;
pthread_create(&t1,nullptr,consumer,&dp);
pthread_create(&t1,nullptr,productor,&dp);
pthread_join(t1,nullptr);
pthread_join(t2,nullptr);
cout<<"线程退出"<

5,结果

我们可以sleep来调整生产数据和使用数据的时间,都是可以的,不会乱的。

基于阻塞队列的生产消费模型_第1张图片

6,码云链接

 lyh_linux-test: linux代码练习。 - Gitee.com

你可能感兴趣的:(c,c++,linux,c++,c语言,后端)