当前位置: 首页 > wzjs >正文

上海网站seo推广营销平台

上海网站seo,推广营销平台,青岛手机网站制作,济南做网站的机构有哪些上篇文章:Linux操作系统7- 线程同步与互斥6(POSIX信号量与环形队列生产者消费者模型)-CSDN博客 本篇代码仓库:myLerningCode/l36 橘子真甜/Linux操作系统与网络编程学习 - 码云 - 开源中国 (gitee.com) 目录 一. 单生产单消费单保…

上篇文章:Linux操作系统7- 线程同步与互斥6(POSIX信号量与环形队列生产者消费者模型)-CSDN博客

本篇代码仓库:myLerningCode/l36 · 橘子真甜/Linux操作系统与网络编程学习 - 码云 - 开源中国 (gitee.com)

目录

一. 单生产单消费单保存模型

1.1 RingQueue.hpp

1.2 Task.hpp

1.3 MainPC.cpp

1.4 测试

二. 多生产多消费模型        

2.1 分析与代码 

2.2 多生产多消费的意义


一. 单生产单消费单保存模型

        通过RingQueue可以实现生产者消费者之间的协同工作,如果现在想要将消费者的输出结果保存在文件中应该怎么办?

        可以定义两个环形队列,三个线程。让消费者充当第二个队列的生产者。

代码如下:

1.1 RingQueue.hpp

        直接使用上篇文件的代码即可。然后我们需要新增一个类,这个类中包含两个环形队列用于消费者同时访问两个队列

#pragma once
#include <iostream>
#include <vector>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
const int gnum = 10; // 阻塞队列的最大容量template <class T>
class RingQueue
{
private:void P(sem_t &sem){// P操作,申请信号量sem--int n = sem_wait(&sem);if (n < 0)std::cerr << "P操作失败" << std::endl;}void V(sem_t &sem){// V操作,释放信号量,sem++int n = sem_post(&sem);if (n < 0)std::cerr << "V操作失败" << std::endl;}public:RingQueue(const int &maxnum = gnum) : _maxnum(maxnum), _ringQueue(gnum){// 在构造函数中完成信号量的初始化int n = sem_init(&_spaceSem, 0, maxnum);if (n < 0)std::cerr << "spaceSem信号量初始化失败" << std::endl;n = sem_init(&_dataSem, 0, 0);if (n < 0)std::cerr << "dataSem信号量初始化失败" << std::endl;_producerStep = _consumerStep = 0;}~RingQueue(){sem_destroy(&_spaceSem);sem_destroy(&_dataSem);}// 生产者插入数据void push(const T &in){// 申请空间资源P(_spaceSem);// 插入数据_ringQueue[_producerStep++] = in;_producerStep %= _maxnum;// 释放一个数据资源V(_dataSem);}// 消费者获取数据void pop(T *out){// 申请数据资源P(_dataSem);// 插入数据*out = _ringQueue[_consumerStep++];_consumerStep %= _maxnum;// 释放一个空间资源V(_spaceSem);}private:std::vector<T> _ringQueue; // 使用数组来实现环形队列size_t _maxnum;sem_t _spaceSem; // 生产者空间资源信号量sem_t _dataSem;  // 消费者数据资源信号量int _producerStep; // 生产者下标int _consumerStep; // 消费者下标
};

1.2 Task.hpp

        需要新增一个保存者的任务

#pragma once
#include <iostream>
#include <cstdio>
#include <functional>class CalTask
{using func_t = std::function<int(int, int, char)>; // func是一个函数// typedef std::function<int(int,int)> func;
public:CalTask() {}CalTask(int x, int y, char op, func_t func): _x(x), _y(y), _op(op), _callback(func) {}std::string operator()(){int result = _callback(_x, _y, _op);char buffer[1024];snprintf(buffer, sizeof(buffer) - 1, "%d %c %d = %d", _x, _op, _y, result);return buffer;}// 返回任务操作的结果std::string toString(){char buffer[1024];snprintf(buffer, sizeof(buffer) - 1, "%d %c %d = ?", _x, _op, _y);return buffer;}private:int _x;int _y;char _op;         // 操作的任务的idfunc_t _callback; // 调用的函数
};class SaveTask
{typedef std::function<void(const std::string &)> func_t;public:SaveTask() {}SaveTask(const std::string &message, func_t func): _message(message), _callback(func) {}void operator()(){_callback(_message);}private:std::string _message; // 保存的信息func_t _callback;     // 将信息写入文件中
};const std::string oper = "+-*/%";
int my_math(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){std::cerr << "div zero" << std::endl;return -1;}else{result = x / y;}break;}case '%':if (y == 0){std::cerr << "moved zero" << std::endl;return -1;}else{result = x % y;}break;default:break;}return result;
}void Save(const std::string &message)
{const std::string task_pwd = "./log.txt";FILE *fp = fopen(task_pwd.c_str(), "a+");if (nullptr == fp){std::cerr << "saver open error" << std::endl;return;}fputs(message.c_str(), fp);fputc('\n', fp);fclose(fp);
}

1.3 MainPC.cpp

#include "RingQueue.hpp"
#include "Task.hpp"
#include <iostream>// 生产者
void *ProductorRoutine(void *args)
{RingQueue<CalTask> *cal_rq = (static_cast<RingQueues<CalTask, SaveTask> *>(args))->_cal_rq;while (true){int x = rand() % 20;int y = rand() % 50;const char op = oper[rand() % oper.size()];CalTask ct(x, y, op, my_math);cal_rq->push(ct);std::cout << "生产者生产任务:" << ct.toString() << " 并传递给消费者完成" << std::endl;}
}// 消费者
void *ConsumerRoutine(void *args)
{RingQueue<CalTask> *cal_rq = (static_cast<RingQueues<CalTask, SaveTask> *>(args))->_cal_rq;RingQueue<SaveTask> *save_rq = (static_cast<RingQueues<CalTask, SaveTask> *>(args))->_save_rq;while (true){CalTask ct;cal_rq->pop(&ct);std::string result = ct();std::cout << "消费者实现任务:" << result << " 实现完成!" << std::endl;SaveTask st(result, Save);save_rq->push(st);std::cout << "消费者传递任务:" << result << " 给保存者完成!" << std::endl;}
}// 保存者
void *SaverRoutine(void *args)
{RingQueue<SaveTask> *save_rq = (static_cast<RingQueues<CalTask, SaveTask> *>(args))->_save_rq;while (true){SaveTask st;save_rq->pop(&st);st();std::cout << "保存者保存任务完成!" << std::endl;}
}void test1()
{RingQueues<CalTask, SaveTask> *rqs = new RingQueues<CalTask, SaveTask>;rqs->_cal_rq = new RingQueue<CalTask>();rqs->_save_rq = new RingQueue<SaveTask>();pthread_t p, c, s;pthread_create(&p, nullptr, ProductorRoutine, rqs);pthread_create(&c, nullptr, ConsumerRoutine, rqs);pthread_create(&s, nullptr, SaverRoutine, rqs);pthread_join(p, nullptr);pthread_join(c, nullptr);pthread_join(s, nullptr);delete rqs;
}int main()
{srand((unsigned int)time(0) ^ getpid() ^ pthread_self());test1();return 0;
}

1.4 测试

        运行结果如下:

二. 多生产多消费模型        

2.1 分析与代码 

        RingQueue环形队列可以保证单个生产者和单个消费者之间的同步与互斥,如果现在有多个生产者和多个消费者的话。如何保证生产者之间的互斥?消费者者之间的互斥?

        阻塞队列中,我们通过加锁的方式让同一时刻只能有一个生产者线程进入临界区或者一个消费者进入临界区。

        而环形队列中, 通过信号量保证了生产者消费者之间的同步与互斥。如果想要保证消费者与消费者之间的互斥,生产者与生产者之间的互斥,也需要加锁保护

        在RingQueue中添加两个成员变量,一个生产者互斥锁,一个消费者互斥锁。同时需要在构造函数中完成锁的初始化,析构函数中完成锁的销毁。

        并且在push函数中加生产者锁,在pop函数中加消费者锁。以实现生产者与生产者之间的互斥和消费者与消费者之间的互斥。(本质是防止多个线程同时访问导致生产者下标或者消费者下标出现数据错误) 

代码如下:

#pragma once
#include <iostream>
#include <vector>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
const int gnum = 10; // 阻塞队列的最大容量template <class T>
class RingQueue
{
private:void P(sem_t &sem){// P操作,申请信号量sem--int n = sem_wait(&sem);if (n < 0)std::cerr << "P操作失败" << std::endl;}void V(sem_t &sem){// V操作,释放信号量,sem++int n = sem_post(&sem);if (n < 0)std::cerr << "V操作失败" << std::endl;}public:RingQueue(const int &maxnum = gnum) : _maxnum(maxnum), _ringQueue(gnum){// 在构造函数中完成信号量的初始化int n = sem_init(&_spaceSem, 0, maxnum);if (n < 0)std::cerr << "spaceSem信号量初始化失败" << std::endl;n = sem_init(&_dataSem, 0, 0);if (n < 0)std::cerr << "dataSem信号量初始化失败" << std::endl;_producerStep = _consumerStep = 0;// 初始化锁pthread_mutex_init(&_pmtx, nullptr);pthread_mutex_init(&_cmtx, nullptr);}~RingQueue(){// 销毁信号量与互斥锁sem_destroy(&_spaceSem);sem_destroy(&_dataSem);//pthread_mutex_destroy(&_cmtx);pthread_mutex_destroy(&_pmtx);}// 生产者插入数据void push(const T &in){// 生产者加锁,保证生产者与生产者之间的互斥pthread_mutex_lock(&_pmtx);// 申请空间资源P(_spaceSem);// 插入数据_ringQueue[_producerStep++] = in;_producerStep %= _maxnum;// 释放一个数据资源V(_dataSem);// 解锁pthread_mutex_unlock(&_pmtx);}// 消费者获取数据void pop(T *out){// 消费者加锁pthread_mutex_lock(&_cmtx);// 申请数据资源P(_dataSem);// 插入数据*out = _ringQueue[_consumerStep++];_consumerStep %= _maxnum;// 释放一个空间资源V(_spaceSem);// 解锁pthread_mutex_unlock(&_cmtx);}private:std::vector<T> _ringQueue; // 使用数组来实现环形队列size_t _maxnum;sem_t _spaceSem; // 生产者空间资源信号量sem_t _dataSem;  // 消费者数据资源信号量int _producerStep; // 生产者下标int _consumerStep; // 消费者下标pthread_mutex_t _pmtx;pthread_mutex_t _cmtx;
};

MainPC.cpp

#include <iostream>
#include <memory>
#include <string>#include <unistd.h>
#include <pthread.h>
#include "RingQueue.hpp"
#include "Task.hpp"const std::string OP = "+-*/%";
void *producer(void *args)
{// 获取交易场所 - 阻塞队列RingQueue<CalTask> *rq = static_cast<RingQueue<CalTask> *>(args);while (true){int x = rand() % 100;int y = rand() % 100;char op = OP[rand() % OP.size()];// 打印日志printf("生产者生产的数据:%d %c %d 并交给消费者计算\n", x, op, y);CalTask ct(x, y, op, my_math);rq->push(ct);}return nullptr;
}void *consumer(void *args)
{// 获取交易场所 - 生产消费阻塞队列,消费保存阻塞队列RingQueue<CalTask> *rq = static_cast<RingQueue<CalTask> *>(args);while (true){// 获取任务计算CalTask ct;rq->pop(&ct);std::string result = ct();std::cout << "消费者获取数据并计算:" << result << std::endl;}return nullptr;
}int main()
{srand((unsigned int)time(0) ^ getpid());// 建立任务队列和保存队列RingQueue<CalTask> *rq = new RingQueue<CalTask>;pthread_t c[3], p[3];pthread_create(&c[0], nullptr, consumer, (void *)rq);pthread_create(&c[1], nullptr, consumer, (void *)rq);pthread_create(&c[2], nullptr, consumer, (void *)rq);pthread_create(&p[0], nullptr, producer, (void *)rq);pthread_create(&p[1], nullptr, producer, (void *)rq);pthread_create(&p[2], nullptr, producer, (void *)rq);pthread_join(c[0], nullptr);pthread_join(c[1], nullptr);pthread_join(c[2], nullptr);pthread_join(p[0], nullptr);pthread_join(p[1], nullptr);pthread_join(p[2], nullptr);delete rq;return 0;
}

测试结果如下:

可以看到,长时间没有出错

如果将加锁解锁操作进行注释:

会出现段错误,因为访问了非法内存

2.2 优化加解锁

        我们申请信号量和释放信号量是原子操作,不需要加锁解锁。无需将这个两个代码放入到临界区。并且一个线程在加锁期间,其他线程是可以申请信号量的!

    // 生产者插入数据void push(const T &in){// 申请空间资源P(_spaceSem);// 生产者加锁,保证生产者与生产者之间的互斥pthread_mutex_lock(&_pmtx);// 插入数据_ringQueue[_producerStep++] = in;_producerStep %= _maxnum;// 解锁pthread_mutex_unlock(&_pmtx);// 释放一个数据资源V(_dataSem);}// 消费者获取数据void pop(T *out){// 申请数据资源P(_dataSem);// 消费者加锁pthread_mutex_lock(&_cmtx);// 插入数据*out = _ringQueue[_consumerStep++];_consumerStep %= _maxnum;// 解锁pthread_mutex_unlock(&_cmtx);// 释放一个空间资源V(_spaceSem);}

        一样一来,一个线程加锁进入临界区之后不会去影响其他线程申请信号量。从而提高整体的效率。 

2.3 多生产多消费的意义

        与阻塞队列BlockQueue一样,多生产多消费的时候。生产线程生产数据可能需要很多时间,一个生产者生产者访问环形队列的时候不妨碍其他线程生产自己的资源。一个消费者访问环形队列拿数据的时候不妨碍其他消费者拿到数据进行处理

http://www.dtcms.com/wzjs/91282.html

相关文章:

  • 建网站 赚钱快速seo整站优化排行
  • 1元涨1000粉丝网站石家庄做网站推广排名的公司
  • 教育网站开发文档广西网站建设制作
  • 网站管理建站湖南长沙今日疫情
  • 牡丹江地区做网站的公司免费推广平台排行榜
  • 焦作会计做继续教育在哪个网站seo和sem的区别是什么?
  • wordpress微信h5电脑优化软件排行榜
  • 如何让网站互动起来今天的热点新闻
  • logo设计免费设计云优化seo软件
  • 在线酒店预定网站制作如何开发一个网站
  • 网站冲突关键词优化哪家好
  • 沈阳学校网站建设武汉网站推广排名
  • 打电话沟通做网站话术四川seo推广方案
  • 有没有做培养基的网站seo渠道
  • 天津滨海新区大爆炸seo点击排名工具
  • 佛山java web网站开发软文文案范文
  • 竹中建设官方网站投广告哪个平台好
  • 网站建设项目实施计划书小程序开发公司
  • 有哪些网站可以做推广seo是对网站进行什么优化
  • 网站源码运行柳市网站制作
  • 法院网站建设方案营销网站建设培训学校
  • 大型门户网站设计公司网站建设开发简介
  • 南昌网站建设精英生哥seo博客
  • 网站开发文件夹百度开户渠道商哪里找
  • 网站初期如何推广的google关键词优化
  • 政务网站建设及安全北京seo编辑
  • 西湖区建设局网站正规seo关键词排名哪家专业
  • 网站域名和邮箱域名广告留电话号的网站
  • 做行业网站广告微信怎么推广
  • 建立网站的过程付费恶意点击软件