Linux操作系统:基于环形队列&信号量实现多生产者多消费者的简易模型

这里没有对信号量进行封装,直接使用了原生库,读者可自行尝试封装,本文代码使用了C++11及以后的语法,智能指针,lambda等,可以尝试自行学习。

test.cc

#include"Thread.hpp"

#include"Ringqueue.hpp"

#include<iostream>

#include<memory>

#include<unistd.h>

#include<functional>

using namespace std;

void Task()

{

char name16;

int ret = pthread_getname_np(pthread_self(), name, sizeof(name));

cout<<"我是一个任务处理我的是"<<name<<endl;

}

using task=function<void()>;

int main()

{

unique_ptr<RingQueue<task>> _queue=make_unique<RingQueue<task>>();

auto productor=\&_queue(){

while(true)

{

sleep(1);

_queue->Push(Task);

}

};

auto consumer=\&_queue(){

while(true)

{

task t;

sleep(1);

_queue->Pop(&t);

t();

}

};

//p是生产者

Thread p1(productor);

Thread p2(productor);

Thread p3(productor);

//c是消费者

Thread c1(consumer);

Thread c2(consumer);

p1.start();

p2.start();

p3.start();

c1.start();

c2.start();

p1.join();

p2.join();

p3.join();

c1.join();

c2.join();

return 0;

}

Thread.hpp

#ifndef THREAD_HPP

#define THREAD_HPP

#include<iostream>

#include<functional>

#include<thread>

#include<string>

#include<unistd.h>

using namespace std;

using func_t=function<void()>;

int gnum=5;

class Thread

{

private:

static void *run(void *argc)

{

Thread *ts=(Thread *)argc;

ts->_func();

}

public:

Thread(func_t t)

:_joinable(false),

_func(t)

{

_name="Thread-"+to_string(gnum--);

}

void start()

{

pthread_create(&_tid,nullptr,&run,this);

pthread_setname_np(_tid,_name.c_str()); //在线程创建的时候设置姓名

_joinable=true;

}

void stop()

{

pthread_cancel(_tid);

}

void join() //阻塞,当线程刚刚创建的时候是可以阻塞的。

{

if(_joinable)

{

pthread_join(_tid,NULL);

}

}

void detach() //分离,当线程可以阻塞就可以分离,但是分离了不能再次阻塞了

{

if (_joinable)

{

pthread_detach(_tid);

_joinable = false;

}

}

~Thread()

{

detach();

}

private:

pthread_t _tid;

string _name;

bool _joinable;

func_t _func;

};

#endif

Ringqueue.hpp

#ifndef RINGQUEUE_HPP

#define RINGQUEUE_HPP

#include<iostream>

#include<vector>

#include<semaphore.h>

#include"Mutex.hpp"

using namespace std;

template<class T>

class RingQueue

{

public:

RingQueue(int cap=5)

:_cap(cap)

,arr(cap)

,p_step(0)

,c_step(0)

{

sem_init(&space_sem,0,cap);

sem_init(&data_sem,0,0);

}

void Push(T in)

{

sem_wait(&space_sem); //p操作,申请信号量

_p_mutex.Lock();

arrp_step=in;

p_step++;

p_step%=_cap;

sem_post(&data_sem); //v操作,释放信号量

_p_mutex.Unlock();

}

void Pop(T *out)

{

sem_wait(&data_sem); //p操作,申请信号量

_c_mutex.Lock();

*out=arrc_step;

c_step++;

c_step%=_cap;

sem_post(&space_sem); //v操作,释放信号量

_c_mutex.Unlock();

}

~RingQueue()

{

sem_destroy(&space_sem);

sem_destroy(&data_sem);

}

private:

int _cap;

vector<T> arr;

sem_t space_sem;

sem_t data_sem;

int p_step;

int c_step;

Mutex _p_mutex;

Mutex _c_mutex;

};

#endif

Mutex.hpp

#ifndef MUTEX_HPP

#define MUTEX_HPP

#include<iostream>

#include<pthread.h>

using namespace std;

class Mutex

{

public:

Mutex()

{

pthread_mutex_init(&_lock,nullptr);

}

void Lock()

{

pthread_mutex_lock(&_lock);

}

void Unlock()

{

pthread_mutex_unlock(&_lock);

}

pthread_mutex_t* GetAddr()

{

return &_lock;

}

~Mutex()

{

pthread_mutex_destroy(&_lock);

}

private:

pthread_mutex_t _lock;

};

#endif

效果:

总结:

  1. 利用信号量 sem_wait 和 sem_post 控制环形队列的读写位置。

  2. 生产者将 Task() 封装为 std::function 推入队列,消费者并发争抢执行。

  3. 利用条件变量/信号量保证队列满时阻塞生产、队列空时阻塞消费,保护临界资源。

相关推荐
小兔子36 分钟前
Linux 的 write 返回成功,数据到底落在哪了
linux
Sayai37 分钟前
Kafka 磁盘空间治理实战:retention 保留策略、log.dirs 在线扩容、分区跨盘迁移与不停机整体换盘
大数据·运维·分布式·kafka·消息队列
我命由我1234539 分钟前
Python 开发 - Python 项目部署(Windows 与 Linux)
java·linux·windows·后端·python·java-ee·python3.11
小羊没烦恼!44 分钟前
TypedSql:在 C# 类型系统上实现一个 SQL 查询引擎
java·linux·运维·服务器·开发语言·数据库·mysql
启博网络1 小时前
智慧档案库房工业组网:MR-1280 边缘大脑,实现温湿度、漏水、防火防盗设备智能联动教程
运维·网络·物联网·网络协议·网络安全·信息与通信·sdwan
海宇数据1 小时前
零信任架构实战:基于海宇普通维保查询构建自动化二手车认证展厅车况质检档案网关
运维·人工智能·架构·自动化
众壹新能源科技1 小时前
储能两充两放日收益增40%,但电池寿命缩短一半,怎么平衡
运维·人工智能·自动化
2401_868534781 小时前
国家东数西算纵深推进的算力感知网络(APN6/算网一体)架构规划与设计
服务器·网络
快乐的韭菜根根1 小时前
SLURM配置服务器一
运维·服务器