Linux:通过线程互斥同步实现基于BlockingQueue的生产消费者模型

一、总体调度:主函数Main.cc

cpp 复制代码
#include "BlockQueue.hpp"
#include "Thread.hpp"
#include <string>
#include <vector>
#include <functional>
#include <unistd.h>
#include <ctime>

using namespace ThreadModule;
int a=10;
using Task = std::function<void()>;
using blockqueue_t=BlockQueue<Task>;

void PrintHello()
{
    std::cout<<"hello world"<<std::endl;
}
void Consumer(blockqueue_t &bq)
{
    while(true)
    {
        //1.从blockqueue取下来任务
        Task t;
        bq.Pop(&t);//pop完成后t中会保存任务
        //2.处理这个任务
        t();//消费者私有
    }
}
void Productor(blockqueue_t &bq)
{
    srand(time(nullptr)^pthread_self());
    int cnt=10;
    while(true)
    {
        sleep(1);
        Task t=PrintHello;
        bq.Enqueue(t);
    }
}
//productor和consumer共用此函数
void StartComm(std::vector<Thread<blockqueue_t>> *threads,int num,blockqueue_t &bq,func_t<blockqueue_t> func)
{
    for(int i=0;i<num;i++)
    {
        std::string name="thread-"+std::to_string(i+1);
        threads->emplace_back(func,bq,name);
        threads->back().Start();
    }
}

//启动consumer
void StartConsumer(std::vector<Thread<blockqueue_t>> *thread,int num,blockqueue_t &bq)
{
    StartComm(thread,num,bq,Consumer);
}

//启动productor
void StartProductor(std::vector<Thread<blockqueue_t>> *thread,int num,blockqueue_t& bq)
{
    StartComm(thread,num,bq,Productor);
}
void WaitAllThread(std::vector<Thread<blockqueue_t>> &threads)
{
    for (auto &thread : threads)
    {
        thread.Join();
    }
}
int main()
{
    blockqueue_t *bq=new blockqueue_t(5);
    std::vector<Thread<blockqueue_t>> threads;

    StartConsumer(&threads,3,*bq);
    StartProductor(&threads,1,*bq);
    WaitAllThread(threads);

    return 0;
}

二、线程功能的封装:Thread.hpp

cpp 复制代码
#ifndef __THREAD_HPP__
#define __THREAD_HPP__

#include <iostream>
#include <string>
#include <unistd.h>
#include <functional>
#include <pthread.h>

namespace ThreadModule
{
    template<typename T>
    using func_t=std::function<void(T&)>;

    template<typename T>
    class Thread
    {
    public:
    void Excute()
    {
        _func(_data);
    }
    public:
    Thread(func_t<T> func,T&data,const std::string& name="none-name")
    :_func(func),_data(data),_threadname(name),_stop(true)
    {}
    static void* threadroutine(void* args)//static成员函数没有this
    {
        Thread<T> *self = static_cast<Thread<T> *>(args);
        self->Excute();
        return nullptr;
    }
    bool Start()
    {
        int n=pthread_create(&_tid,nullptr,threadroutine,this);//把this传给threadroutine让其完成调用
        if(!n)
        {
            _stop=false;
            return true;
        }
        else
        {
            return false;
        }
    }
    void Detach()
    {
        if(!_stop)
        {
            pthread_detach(_tid);
        }
    }
    void Join()
    {
        if(!_stop)
        {
            pthread_join(_tid,nullptr);
        }
    }
    std::string name()
    {
        return _threadname;
    }
    void Stop()
    {
        _stop = true;
    }
    ~Thread()
    {}
    private:
    pthread_t _tid;
    std::string _threadname;
    T& _data;//要传入所执行函数的参数
    func_t<T> _func;//线程要执行的函数
    bool _stop;
    };
}
#endif

三、临界区阻塞队列:BlockQueue

cpp 复制代码
#ifndef __BLOCK_QUEUE_HPP__
#define __BLOCK_QUEUE_HPP__

#include <iostream>
#include <string>
#include <queue>
#include <pthread.h>

template <typename T>
class BlockQueue
{
private:
    bool IsFull()
    {
        return _block_queue.size()==_cap;
    }
    bool IsEmpty()
    {
        return _block_queue.empty();
    }
public:
BlockQueue(int cap):_cap(cap)
{
    _productor_wait_num=0;
    _consumer_wait_num=0;
    pthread_mutex_init(&_mutex,nullptr);
    pthread_cond_init(&_product_cond,nullptr);
    pthread_cond_init(&_consum_cond,nullptr);
}
void Enqueue(T& in)
{
    pthread_mutex_lock(&_mutex);
    while(IsFull())//保证代码的健壮性
    {
         // 生产线程去等待,是在临界区中休眠的!你现在还持有锁呢!!!
        // 1. pthread_cond_wait调用是: a. 让调用线程等待 b. 自动释放曾经持有的_mutex锁 c. 当条件满足,线程唤醒,pthread_cond_wait要求线性
        // 必须重新竞争_mutex锁,竞争成功,方可返回!!!
        // 之前:安全
        _productor_wait_num++;
        pthread_cond_wait(&_product_cond,&_mutex);//  只要等待,必定会有唤醒,唤醒的时候,就要继续从这个位置向下运行!!
        _productor_wait_num--;
    }
    //进行生产
    _block_queue.push(in);
    if(_consumer_wait_num > 0)
            pthread_cond_signal(&_consum_cond); // pthread_cond_broadcast
        pthread_mutex_unlock(&_mutex);
}
void Pop(T *out)
{
    pthread_mutex_lock(&_mutex);
    while(IsEmpty())
    {
    // 消费线程去等待,是在临界区中休眠的!你现在还持有锁呢!!!
    // 1. pthread_cond_wait调用是: a. 让调用进程等待 b. 自动释放曾经持有的_mutex锁
        _consumer_wait_num++;
        pthread_cond_wait(&_consum_cond, &_mutex);  // 伪唤醒
        _consumer_wait_num--;
    }
    //进行消费
    *out=_block_queue.front();//让main.cc的消费者拿到任务
    _block_queue.pop();
    //通知生产者生产
    if(_productor_wait_num>0)
        pthread_cond_signal(&_product_cond);
    pthread_mutex_unlock(&_mutex);
}
~BlockQueue()
{
    pthread_mutex_destroy(&_mutex);
    pthread_cond_destroy(&_product_cond);
    pthread_cond_destroy(&_consum_cond);

}
private:
    std::queue<T> _block_queue;//阻塞队列,是被整体使用的
    int _cap;//总上限
    pthread_mutex_t _mutex;//保护_block_queue的锁
    pthread_cond_t _product_cond;//专门给生产者提供的条件变量
    pthread_cond_t _consum_cond;//专门给生产者提供的条件变量

    int _productor_wait_num;
    int _consumer_wait_num;

};
#endif

四、Makefile

cpp 复制代码
cp:Main.cc
	g++ -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:
	rm -f cp
相关推荐
段帅龙呀1 小时前
Redis构建缓存服务器
服务器·redis·缓存
乌鸦不像写字台2 小时前
【docker部署】在服务器上使用docker
服务器·docker·容器
牧以南歌〆3 小时前
在Ubuntu主机中修改ARM Linux开发板的根文件系统
linux·arm开发·驱动开发·ubuntu
互联网搬砖老肖3 小时前
运维打铁: MongoDB 数据库集群搭建与管理
运维·数据库·mongodb
Antonio9154 小时前
【音视频】HLS简介与服务器搭建
运维·服务器·音视频
夜月yeyue4 小时前
设计模式分析
linux·c++·stm32·单片机·嵌入式硬件
kfepiza5 小时前
Debian的`/etc/network/interfaces`的`allow-hotplug`和`auto`对比讲解 笔记250704
linux·服务器·网络·笔记·debian
无妄-20245 小时前
软件架构升级中的“隐形地雷”:版本选型与依赖链风险
java·服务器·网络·经验分享
艾伦_耶格宇5 小时前
【docker】-1 docker简介
运维·docker·容器
R.X. NLOS5 小时前
VS Code远程开发新方案:使用SFTP扩展解决Remote-SSH连接不稳定问题
运维·服务器·ssh·debug·vs code