基于BlockQueue的生产消费模型及Linux中的信号量

2024-08-22 01:36

本文主要是介绍基于BlockQueue的生产消费模型及Linux中的信号量,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 基于BlockQueue的生产消费模型

Task.hpp

#pragma once#include<cstdio>
#include<iostream>
#include<string>
#include<functional>using namespace std;
class CalTask
{using func_t=function<int(int,int,char)>;//typedef function<int(int,int)> func_t;
public:CalTask(){}CalTask(int x,int y,char op,func_t func):_x(x),_y(y),_op(op),_callback(func){}string operator()(){int result=_callback(_x,_y,_op);//构建结构字符串char buffer[1024];snprintf(buffer,sizeof buffer,"%d %c %d = %d ",_x,_op,_y,result);return buffer;}string toTaskString(){char buffer[1024];snprintf(buffer,sizeof buffer,"%d %c %d = ? ",_x,_op,_y);return buffer;}
private:int _x;int _y;char _op;func_t _callback;
};int mymath(int x,int y,char op)
{int result=0;switch(op){case '+':result=x+y;break;case '-':result=x-y;break;case '*':result=x*y;break;case '/':{if(y==0){cerr<<"div zero error!!!"<<endl;result=-1;}elseresult=x/y;}break;case '%':{if(y==0){cerr<<"mod zero error!!!"<<endl;result=-1;}elseresult=x%y;}          break;default:break;}return result;
}
class SaveTask
{typedef function<void(const string&)> func_t;
public:SaveTask(){}SaveTask(const string &message,func_t func):_message(message),_func(func){}void operator()(){_func(_message);}
private:string _message;func_t _func;
};
void Save(const string& message)
{const string target="./log.txt";FILE* fp=fopen(target.c_str(),"a+");if(!fp){cerr<<" fopen error "<<endl;return;} fputs(message.c_str(),fp);fputs("\n",fp);fclose(fp);
}

Block.hpp

#pragma once#include<iostream>
#include<queue>
#include<pthread.h>
#include<unistd.h>using namespace std;
static const int gmaxcap=5;
template<class T>
class BlockQueue
{
public:BlockQueue(const int& maxcap=gmaxcap):_maxcap(maxcap){pthread_mutex_init(&_mutex,nullptr);pthread_cond_init(&_pcond,nullptr);pthread_cond_init(&_ccond,nullptr);}void push(const T& in)//输入型参数,const &{pthread_mutex_lock(&_mutex);//1.判断while(is_full())//防止向队列中多push内容->提高代码健壮性{//生产条件不满足无法生产,需要生产者等待//临界区://pthread_cond_wait   第二个参数必须为我们正在使用的互斥锁!!!//1.pthread_cond_wait:该函数在调用的时候,会以原子性的方式将锁释放,并将自己挂起//2.pthread_cond_wait:该函数在被唤醒返回的时候,会自动重新获取当时传入的锁pthread_cond_wait(&_pcond,&_mutex);}_q.push(in);//pthread_cond_signal即可在临界区内也可在临界区外pthread_cond_signal(&_ccond);pthread_mutex_unlock(&_mutex);}void pop(T* out)//输出型参数:*, // 输入输出型:&{pthread_mutex_lock(&_mutex);while(is_empty()){pthread_cond_wait(&_ccond,&_mutex);}*out=_q.front();_q.pop();pthread_cond_signal(&_pcond);pthread_mutex_unlock(&_mutex);}~BlockQueue(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_pcond);pthread_cond_destroy(&_ccond);}
private:bool is_empty() {return _q.empty();}bool is_full(){return _q.size()==_maxcap;}queue<T> _q;int _maxcap;//队列中元素的上限pthread_mutex_t _mutex;pthread_cond_t _pcond;//生产者对应的条件变量pthread_cond_t _ccond;//消费者对应的条件变量
}; 

Main.cc

#include<time.h>
#include<cstring>
#include<sys/types.h>
#include"BlockQueue.hpp"
#include"Task.hpp"const string oper="+-*/%";template<class C,class S>
class BlockQueues
{
public:BlockQueue<C> *c_bq;BlockQueue<S> *s_bq;
};
void* productor(void *bqs_)
{//BlockQueue<Task>* bq=static_cast<BlockQueue<Task>*>(bq_);BlockQueue<CalTask>* bq= (static_cast<BlockQueues<CalTask,SaveTask>*>(bqs_))->c_bq;while(true){//生产活动-x先用随机数构建数据int x=rand()%10+1;int y=rand()%5;int operCode=rand()%oper.size();CalTask t(x,y,oper[operCode],mymath);bq->push(t);cout<<"productor thread,生产数据: "<<t.toTaskString()<<endl;sleep(1);}return nullptr;
}
void *consumer(void* bqs_)
{//BlockQueue<Task>* bq=static_cast<BlockQueue<Task>*>(bq_);BlockQueue<CalTask>* bq= (static_cast<BlockQueues<CalTask,SaveTask>*>(bqs_))->c_bq;BlockQueue<SaveTask>* save_bq= (static_cast<BlockQueues<CalTask,SaveTask>*>(bqs_))->s_bq;while(true){//消费活动CalTask t;bq->pop(&t);string result=t();cout<<"消费任务: "<<result<<endl;// SaveTask save(result,Save);// save_bq->push(save);// cout<<"consumer thread,推送保存任务完成..."<<endl;}return nullptr;
}void* saver(void* bqs_)
{BlockQueue<SaveTask>* save_bq= (static_cast<BlockQueues<CalTask,SaveTask>*>(bqs_))->s_bq;while(true){SaveTask t;save_bq->pop(&t);t();cout<<"保存任务完成..."<<endl;}
}
int main()
{srand((unsigned long)time(nullptr));BlockQueues<CalTask,SaveTask> bqs;bqs.c_bq=new BlockQueue<CalTask>();bqs.s_bq=new BlockQueue<SaveTask>();pthread_t c,p,s;pthread_create(&c,nullptr,consumer,&bqs);pthread_create(&p,nullptr,productor,&bqs);// pthread_create(&s,nullptr,saver,&bqs);pthread_join(c,nullptr);pthread_join(p,nullptr);// pthread_join(s,nullptr);delete bqs.c_bq;delete bqs.s_bq;return 0;
}

POSIX信号量

POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。 但POSIX可以用于 线程间同步。 

        pthread_mutex_lock(&_mutex);while(is_empty()){pthread_cond_wait(&_ccond,&_mutex);}*out=_q.front();_q.pop();pthread_cond_signal(&_pcond);pthread_mutex_unlock(&_mutex);

该段代码涉及到对临界资源的访问:

1.一个线程,在操作临界资源的时候,临界资源必须是满足条件的!!!

2.公共资源是否满足生产或消费条件【我们在访问之前无法得知】

3.只能先加锁,在检测,在操作,在解锁

只要对资源进行整体加锁,就默认对资源整体使用,但是实际情况可能是一份公共资源允许同时访问不同区域。

什么事信号量???

a.信号量本质是一个计数器。->用来衡量有多少临界资源。

b.只要拥有信号量,就在未来一定能够拥有临界资源的一部分。

申请信号量的本质:对临界资源中特定小块资源的预定机制。

通过信号量可以在访问临界资源之前,提前知道临界资源的使用情况。

信号量的核心操作:PV原语

sudo find ../../ -name Thread.hpp
cp ../../lesson32/Thread.hpp .

这篇关于基于BlockQueue的生产消费模型及Linux中的信号量的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1094868

相关文章

Java Lettuce 客户端入门到生产的实现步骤

《JavaLettuce客户端入门到生产的实现步骤》本文主要介绍了JavaLettuce客户端入门到生产的实现步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要... 目录1 安装依赖MavenGradle2 最小化连接示例3 核心特性速览4 生产环境配置建议5 常见问题

linux ssh如何实现增加访问端口

《linuxssh如何实现增加访问端口》Linux中SSH默认使用22端口,为了增强安全性或满足特定需求,可以通过修改SSH配置来增加或更改SSH访问端口,具体步骤包括修改SSH配置文件、增加或修改... 目录1. 修改 SSH 配置文件2. 增加或修改端口3. 保存并退出编辑器4. 更新防火墙规则使用uf

Linux join命令的使用及说明

《Linuxjoin命令的使用及说明》`join`命令用于在Linux中按字段将两个文件进行连接,类似于SQL的JOIN,它需要两个文件按用于匹配的字段排序,并且第一个文件的换行符必须是LF,`jo... 目录一. 基本语法二. 数据准备三. 指定文件的连接key四.-a输出指定文件的所有行五.-o指定输出

Linux jq命令的使用解读

《Linuxjq命令的使用解读》jq是一个强大的命令行工具,用于处理JSON数据,它可以用来查看、过滤、修改、格式化JSON数据,通过使用各种选项和过滤器,可以实现复杂的JSON处理任务... 目录一. 简介二. 选项2.1.2.2-c2.3-r2.4-R三. 字段提取3.1 普通字段3.2 数组字段四.

Linux kill正在执行的后台任务 kill进程组使用详解

《Linuxkill正在执行的后台任务kill进程组使用详解》文章介绍了两个脚本的功能和区别,以及执行这些脚本时遇到的进程管理问题,通过查看进程树、使用`kill`命令和`lsof`命令,分析了子... 目录零. 用到的命令一. 待执行的脚本二. 执行含子进程的脚本,并kill2.1 进程查看2.2 遇到的

Linux云服务器手动配置DNS的方法步骤

《Linux云服务器手动配置DNS的方法步骤》在Linux云服务器上手动配置DNS(域名系统)是确保服务器能够正常解析域名的重要步骤,以下是详细的配置方法,包括系统文件的修改和常见问题的解决方案,需要... 目录1. 为什么需要手动配置 DNS?2. 手动配置 DNS 的方法方法 1:修改 /etc/res

Linux创建服务使用systemctl管理详解

《Linux创建服务使用systemctl管理详解》文章指导在Linux中创建systemd服务,设置文件权限为所有者读写、其他只读,重新加载配置,启动服务并检查状态,确保服务正常运行,关键步骤包括权... 目录创建服务 /usr/lib/systemd/system/设置服务文件权限:所有者读写js,其他

Linux下利用select实现串口数据读取过程

《Linux下利用select实现串口数据读取过程》文章介绍Linux中使用select、poll或epoll实现串口数据读取,通过I/O多路复用机制在数据到达时触发读取,避免持续轮询,示例代码展示设... 目录示例代码(使用select实现)代码解释总结在 linux 系统里,我们可以借助 select、

Linux挂载linux/Windows共享目录实现方式

《Linux挂载linux/Windows共享目录实现方式》:本文主要介绍Linux挂载linux/Windows共享目录实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录文件共享协议linux环境作为服务端(NFS)在服务器端安装 NFS创建要共享的目录修改 NFS 配

linux系统中java的cacerts的优先级详解

《linux系统中java的cacerts的优先级详解》文章讲解了Java信任库(cacerts)的优先级与管理方式,指出JDK自带的cacerts默认优先级更高,系统级cacerts需手动同步或显式... 目录Java 默认使用哪个?如何检查当前使用的信任库?简要了解Java的信任库总结了解 Java 信