ToB企服应用市场:ToB评测及商务社交产业平台

标题: 【Linux】生产消耗模子实践 --- 基于信号量的环形队列 [打印本页]

作者: 美食家大橙子    时间: 2024-8-24 14:39
标题: 【Linux】生产消耗模子实践 --- 基于信号量的环形队列

   你送出去的每颗糖都去了该去的地方,      其实地球是圆的,        你做的功德终会回到你身上。           --- 何炅 ---            

  
1 信号量

信号量本质是一个计数器,可以在初始化时对设置资源数目,进程 / 线程 可以获取信号量来对资源举行操纵和结束操纵可以开释信号量!
用于多进程 / 多线程 对共享数据对象的读取,它和管道有所差别,它不以传送数据为重要目的,它重要是用来保护共享资源(信号量也属于临界资源),使得资源在一个时刻只有一个进程独享。 在资源只有一个时就一把互斥锁!
信号量只能举行两种操纵获取等待和开释信号,即PV操纵:
PV操纵都是原子的,不消担心线程安全!此外信号量初始化和烧毁的接口是:
2 框架构建

6.为什么信号量不加条件判断?:
在环形队列的实现中,没有使用条件变量,像阻塞队列一样举行条件的判断 而是直接来不管三七二十一举行获取信号量,因为信号量本身就是判断条件,信号量是用来描述内部资源的多少的,是原子的!本质是一个计数器 通过预订机制来保证内部资源的公道使用,当信号量的资源数目为1时和锁时等价的!
3 代码实现

  1. #pragma once
  2. #include <vector>
  3. #include <semaphore.h>
  4. const int default_cap = 5;
  5. template <class T>
  6. class RingQueue
  7. {
  8. public:
  9.     RingQueue(int max_cap = default_cap) : _rq(max_cap), _max_cap(max_cap), _p_step(0), _c_step(0)
  10.     {
  11.         // 信号量初始化
  12.         sem_init(&_space_sem, 0, _max_cap);
  13.         sem_init(&_data_sem, 0, 0);
  14.         //锁进行初始化
  15.         pthread_mutex_init(&_c_mtx , nullptr);
  16.         pthread_mutex_init(&_p_mtx , nullptr);
  17.     }
  18.     // 获取信号量
  19.     void P(sem_t &sp)
  20.     {
  21.         sem_wait(&sp);
  22.     }
  23.     // 释放信号量
  24.     void V(sem_t &sp)
  25.     {
  26.         sem_post(&sp);
  27.     }
  28.     // 插入操作
  29.     void Push(const T &t)
  30.     {
  31.         // 获取空间信号量 --
  32.         P(_space_sem);
  33.         //临界区上锁
  34.         pthread_mutex_lock(&_p_mtx );
  35.         _rq[_p_step] = t;
  36.         _p_step++;
  37.         _p_step %= _max_cap;
  38.         //解锁
  39.         pthread_mutex_unlock(&_p_mtx);
  40.         // 释放信号量 ++
  41.         V(_data_sem);
  42.     }
  43.     // 获取操作
  44.     void Pop(T *t)
  45.     {
  46.         // 获取资源信号量
  47.         P(_data_sem);
  48.         pthread_mutex_lock(&_c_mtx);
  49.         *t = _rq[_c_step];
  50.         _c_step++;
  51.         _c_step %= _max_cap;
  52.         pthread_mutex_unlock(&_c_mtx);
  53.         // 释放信号量
  54.         V(_space_sem);
  55.     }
  56.     ~RingQueue()
  57.     {
  58.         // 销毁对应信号量!
  59.         sem_destroy(&_space_sem);
  60.         sem_destroy(&_data_sem);
  61.         //锁进行释放
  62.         pthread_mutex_destroy(&_c_mtx);
  63.         pthread_mutex_destroy(&_p_mtx);
  64.     }
  65. private:
  66.     // 底层线性结构,模拟环形队列
  67.     std::vector<T> _rq;
  68.     // 最大容量
  69.     int _max_cap;
  70.     // 生产者/消费者 下标
  71.     int _p_step;
  72.     int _c_step;
  73.     // 空间/资源 信号量
  74.     sem_t _space_sem;
  75.     sem_t _data_sem;
  76.     // 生产 / 消费 锁
  77.     pthread_mutex_t _p_mtx;
  78.     pthread_mutex_t _c_mtx;
  79. };
复制代码
4 测试运行

我们来做一些简单测试,我们计划了Task类,用于执行加法操纵。它包含两个整型参数_x和_y,并提供方法来执行加法并获取效果。通过重载括号运算符,Task对象可以被直接调用以执行计算。此外,类还提供了调试信息和效果输出的功能。
我写了一段代码段用于测试。在该测试中:界说了两个线程函数Consumer和Productor,分别模仿消耗者和生产者行为:
主函数main中创建了一个容量为5的RingQueue<Task>实例,并启动了两个线程。pthread_create用于创建线程,pthread_join确保主线程等待子线程执行完毕。通过这种方式,我们验证了环形队列在多线程环境下的线程安全性和功能精确性。
  1. #include <iostream>
  2. #include "RingQueue.hpp"
  3. #include <pthread.h>
  4. #include <stdlib.h>
  5. #include <sys/types.h>
  6. #include <unistd.h>
  7. #include "Task.hpp"
  8. void *Consumer(void *args)
  9. {
  10.     RingQueue<Task> *rq = static_cast<RingQueue<Task> *>(args);
  11.     srand(time(nullptr) ^ getpid());
  12.     while (true)
  13.     {
  14.         // 不断的进行获取
  15.         Task data ;
  16.         rq->Pop(&data);
  17.         data();
  18.         std::cout << "Consumer 消费者消费 -> " << data.result() << std::endl;
  19.         sleep(1);
  20.     }
  21. }
  22. void *Productor(void *args)
  23. {
  24.     RingQueue<Task> *rq = static_cast<RingQueue<Task> *>(args);
  25.     srand(time(nullptr) ^ getpid());
  26.     while (true)
  27.     {
  28.         // 不断的进行写入
  29.         int num1 = rand() % 10;
  30.         usleep(1000);
  31.         int num2 = rand() % 10;
  32.         Task t(num1 , num2);
  33.         rq->Push(t);
  34.         std::cout << "Productor 生产者生产 -> " << t.debug() << std::endl;
  35.         usleep(10000);
  36.     }
  37. }
  38. int main()
  39. {
  40.     // 环形队列
  41.     RingQueue<Task> rq(5);
  42.     // 使用两个线程来测试
  43.     pthread_t t1, t2;
  44.     pthread_create(&t1, nullptr, Consumer, &rq);
  45.     pthread_create(&t2, nullptr, Productor, &rq);
  46.     pthread_join(t1, nullptr);
  47.     pthread_join(t2, nullptr);
  48. }
复制代码
运行效果:

很好的完成了任务!!!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4