当前位置:   article > 正文

【C++】多线程(链式、循环队列)实现生产者消费者模式_c++ 生产者消费者模型 多线程

c++ 生产者消费者模型 多线程

生产者消费者模式:

        生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了两个共享固定大小缓冲区的线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
 

这是我在Linux多线程中写过的一篇文章,里面详细讲解了信号量和互斥锁解决多线程的生产者与消费者模式:

Linux信号量与互斥锁解决生产者与消费者问题_神厨小福贵!的博客-CSDN博客先来看什么是生产者消费者问题:生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了两个共享固定大小缓冲区的线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据https://blog.csdn.net/qq_45829112/article/details/121580819下图就是生产者消费者的大致模型:

上图所示,我们能不能在【C++】中使用多线程使得,一边生产,一边消费呢???

关于【C++】多线程,我在之前一篇中说过:

【C++】多线程thread_神厨小福贵!的博客-CSDN博客进程和线程的区别:进程是资源分配的最小单位,线程是CPU调度的最小单位 进程有自己的独立地址空间,线程共享进程中的地址空间 进程的创建消耗资源大,线程的创建相对较小进程的切换开销大,线程的切换开销相对较小 进程:程序执行的过程叫进程。线程:进程内部的一条执行序列或执行路径,一个进程可以包含多条线程(多线程)!每个进程最少有一个线程,例如下面代码:#include <iostream>using namespace std; int main(){ https://blog.csdn.net/qq_45829112/article/details/123521502?spm=1001.2014.3001.5502

下面我们拿链队列循环队列分别实现我们的生产者消费者模式

链队列实现生产者消费者:queue来实现:

  1. const int MAX_ITEM = 20; //双端队列最大长度
  2. std::mutex mx; //全局锁
  3. std::condition_variable cv; //条件变量cv
  4. class Queue
  5. {
  6. public:
  7. void put(int val, int index) //入队函数
  8. {
  9. std::unique_lock<std::mutex> lock(mx); //类似于智能指针的智能锁,不需要手动解锁
  10. while (q.size() == MAX_ITEM) //队列满了之后,等待
  11. {
  12. cv.wait(lock);
  13. }
  14. q.push_back(val); //入队
  15. cv.notify_all(); //唤醒
  16. cout << "producer: " << index << "val : " << "生产者" << val << endl;
  17. }
  18. int get(int index) //出队函数
  19. {
  20. unique_lock<std::mutex> lock(mx); //类似于智能指针的智能锁,不需要手动解锁
  21. while (q.empty()) //队列空了等待
  22. {
  23. cv.wait(lock);
  24. }
  25. int val = q.front(); //出队函数
  26. q.pop_front(); //队头出,队尾加
  27. cv.notify_all();
  28. cout << "Consumer : " << index << " val : " << val << endl;
  29. return val;
  30. }
  31. private:
  32. deque<int> q;
  33. };
  34. void producer(Queue* q, int index)
  35. {
  36. for (int i = 0; i < 100; ++i)
  37. {
  38. q->put(i, index); //调用class queue中的put函数
  39. std::this_thread::sleep_for(std::chrono::milliseconds(100));
  40. }
  41. }
  42. void consumer(Queue* q, int index)
  43. {
  44. for (int i = 0; i < 100; ++i)
  45. {
  46. q->get(index); //调用class queue中的get函数
  47. std::this_thread::sleep_for(std::chrono::milliseconds(100));
  48. }
  49. }
  50. int main()
  51. {
  52. Queue* q = new Queue();
  53. thread p1(producer, q, 1);
  54. thread s1(consumer, q, 1);
  55. p1.join();
  56. s1.join();
  57. return 0;
  58. }

这个代码也比较简单,就不多说了,上面注释也很详细!!!

看一下运行结果:因为我在消费者函数和生产者函数中的睡眠时间都是100,所以我们的生产者和消费者就是生产一个,消费一个这个情况

 循环队列实现生产者消费者:

下图是循环队列的大致示意图:

 下面来代码:

  1. template<class T> //模板类
  2. class Queue
  3. {
  4. enum { QUSIZE = 8 }; //循环队列大小为8
  5. T* data; //指针指向循环队列连续空间
  6. int front; //队头
  7. int rear; //队尾
  8. int size; //当前队列的元素个数
  9. int maxsize; //队列最大大小
  10. public:
  11. Queue() :data(nullptr), front(0), rear(0), size(0), maxsize(QUSIZE)
  12. {
  13. data = new T[maxsize];
  14. }
  15. ~Queue()
  16. {
  17. free(data);
  18. data = nullptr;
  19. front = rear = -1;
  20. size = 0;
  21. maxsize = 0;
  22. }
  23. int Capt() const { return maxsize; } //求队列最大元素个数的函数
  24. int Size() const { return size; } //求现有元素个数的函数
  25. bool Empty() const { return Size() == 0; } //判空函数
  26. bool Full() const { //判满函数
  27. return Size() == maxsize;
  28. }
  29. bool Push(const T& val) //入队函数
  30. {
  31. if (Full()) return false;
  32. data[rear] = val;
  33. rear = (rear + 1) % maxsize; //上面说到最大值为8,也就是说存储下标为0到7
  34. size += 1;
  35. return true;
  36. }
  37. bool Front(T& val) //出队函数
  38. {
  39. if (Empty()) return false;
  40. val = data[front];
  41. front = (front + 1) % maxsize;//上面说到最大值为8,也就是说存储下标为0到7
  42. size -= 1;
  43. return true;
  44. }
  45. };
  46. Queue<int> iq; //实例化iq
  47. std::mutex mx; //全局锁mx
  48. std::condition_variable cv; //条件变量cv
  49. const int maxsize = iq.Capt(); //最大元素个数
  50. int number = 0; // 100;
  51. void producer(int index)
  52. {
  53. std::unique_lock<std::mutex> lock(mx); //类似于智能指针的智能锁
  54. for (int i = 0; i < 100; i++)
  55. {
  56. cv.wait(lock, []()->bool {return !iq.Full(); }); //lambda表达式
  57. iq.Push(number); //上述lambda表达式为真退出,所以就不为full时为退出
  58. cout << "product " << number << endl;
  59. number++;
  60. cv.notify_all();
  61. }
  62. }
  63. void consumer(int index)
  64. {
  65. std::unique_lock<std::mutex> lock(mx);
  66. for (int i = 0; i < 100; i++)
  67. {
  68. cv.wait(lock, []()->bool {return !iq.Empty(); });//lambda表达式中为真退出等待不为NULL时,退出wait
  69. int val = 0;
  70. iq.Front(val);
  71. cout << "consumer " << val << endl;
  72. cv.notify_all();
  73. }
  74. }
  75. int main()
  76. {
  77. std::thread pth1(producer, 1); //生产者
  78. std::thread pth2(consumer, 2); //消费者
  79. pth1.join();
  80. pth2.join();
  81. return 0;
  82. }

运行结果: 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/267901
推荐阅读
相关标签
  

闽ICP备14008679号