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. 利用条件变量/信号量保证队列满时阻塞生产、队列空时阻塞消费,保护临界资源。

相关推荐
梦到啥说啥6 分钟前
Linux网络编程:UDP网络协议
linux·网络协议·udp
无足鸟ICT1 小时前
【RHCA+】ipcrm命令(删除IPC资源)
linux
星恒随风2 小时前
MySQL 从零入门(四):表的增删改查与基础查询详解
linux·服务器·数据库·笔记·mysql
Z21-X3 小时前
云原生运维学习笔记:ECShop全链路容器化部署与Docker底层资源隔离深度剖析
运维·笔记·学习·云原生
陈希1235 小时前
Linux 网络诊断工具实战:ping、curl、tcpdump、mtr、dig 用法详解
linux·网络·tcpdump
进击的荆棘5 小时前
Linux系统——线程同步与互斥之线程池和安全
linux·运维·服务器·安全·单例模式·线程
刘大望6 小时前
不用买开发板:用 QEMU 走通嵌入式 Linux 全流程(三)存储与网络实地验证
linux·qemu·嵌入式linux·alpine
蜗牛一步步往上爬6 小时前
第六章 BMC常用的调试工具
服务器
小李不困还能学6 小时前
VSCode_RemoteSSH_一直卡在下载服务器_解决教程
服务器·ide·vscode
瑞雪兆丰年兮6 小时前
JavaWeb-day11-SpringBootWeb案例(文件上传)
java·运维·服务器·javaweb·oss