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

news/2024/7/8 7:59:42

生产者消费者模式:

        生产者消费者问题(英语: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来实现:

const int MAX_ITEM = 20;  //双端队列最大长度
std::mutex mx;   //全局锁
std::condition_variable cv;  //条件变量cv
class Queue  
{
public:void put(int val, int index)  //入队函数{std::unique_lock<std::mutex> lock(mx);  //类似于智能指针的智能锁,不需要手动解锁while (q.size() == MAX_ITEM)  //队列满了之后,等待{cv.wait(lock);}q.push_back(val);  //入队cv.notify_all();  //唤醒cout << "producer: " << index << "val : " << "生产者" << val << endl;} int get(int index)   //出队函数{unique_lock<std::mutex> lock(mx); //类似于智能指针的智能锁,不需要手动解锁while (q.empty())  //队列空了等待{cv.wait(lock);}int val = q.front();  //出队函数q.pop_front();  //队头出,队尾加cv.notify_all();cout << "Consumer : " << index << " val : " << val << endl;return val;}
private:deque<int> q;
};
void producer(Queue* q, int index)
{for (int i = 0; i < 100; ++i){q->put(i, index);  //调用class queue中的put函数std::this_thread::sleep_for(std::chrono::milliseconds(100));}
}
void consumer(Queue* q, int index)
{for (int i = 0; i < 100; ++i){q->get(index);  //调用class queue中的get函数std::this_thread::sleep_for(std::chrono::milliseconds(100));}
}
int main()
{Queue* q = new Queue();thread p1(producer, q, 1);thread s1(consumer, q, 1);p1.join();s1.join();return 0;
}

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

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

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

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

 下面来代码:

template<class T>  //模板类
class Queue
{enum { QUSIZE = 8 };  //循环队列大小为8T* data;   //指针指向循环队列连续空间int front;  //队头int rear;  //队尾int size;  //当前队列的元素个数int maxsize;  //队列最大大小
public:Queue() :data(nullptr), front(0), rear(0), size(0), maxsize(QUSIZE){data = new T[maxsize];}~Queue(){free(data);data = nullptr;front = rear = -1;size = 0;maxsize = 0;}int Capt() const { return maxsize; }    //求队列最大元素个数的函数int Size() const { return size; }   //求现有元素个数的函数bool Empty() const { return Size() == 0; }  //判空函数bool Full() const {             //判满函数return Size() == maxsize;}bool Push(const T& val)     //入队函数{if (Full()) return false;data[rear] = val;rear = (rear + 1) % maxsize;  //上面说到最大值为8,也就是说存储下标为0到7size += 1;return true;}bool Front(T& val)  //出队函数{if (Empty()) return false;val = data[front];front = (front + 1) % maxsize;//上面说到最大值为8,也就是说存储下标为0到7size -= 1;return true;}
};Queue<int> iq;  //实例化iq
std::mutex mx;  //全局锁mx
std::condition_variable cv;  //条件变量cv
const int maxsize = iq.Capt();  //最大元素个数int number = 0; // 100;
void producer(int index)
{std::unique_lock<std::mutex> lock(mx);  //类似于智能指针的智能锁for (int i = 0; i < 100; i++){cv.wait(lock, []()->bool {return !iq.Full(); });  //lambda表达式iq.Push(number);  //上述lambda表达式为真退出,所以就不为full时为退出cout << "product " << number << endl;number++;cv.notify_all();}
}void consumer(int index)
{std::unique_lock<std::mutex> lock(mx);for (int i = 0; i < 100; i++){cv.wait(lock, []()->bool {return !iq.Empty(); });//lambda表达式中为真退出等待不为NULL时,退出waitint val = 0;iq.Front(val);cout << "consumer " << val << endl;cv.notify_all();}
}int main(){std::thread pth1(producer, 1);  //生产者std::thread pth2(consumer, 2);  //消费者pth1.join();pth2.join();return 0;
}

运行结果: 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pgtn.cn/news/15573.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

添加引用方式抛出和捕获干净的WebService异常

转载&#xff1a;http://www.cnblogs.com/ahdung/p/3953431.html 说明&#xff1a;【干净】指的是客户端在捕获WebService&#xff08;下称WS&#xff09;抛出的异常时&#xff0c;得到的ex.Message就是WS方法中抛出的异常消息&#xff0c;不含任何“杂质”。 前提&#xff1a;…

基数排序(桶排序)

基数排序又叫桶排序&#xff1a; 先按照个位数排序&#xff0c;第一次排序好之后&#xff1b;再次按照十位数进行排序&#xff0c;第二次排序好之后&#xff1b;第三次对百位进行排序.................. 实现原理图&#xff1a;拿出一些个类似“桶”的东西 将分别按照个位&am…

如何用两个栈实现一个队列?

先看分析 来看代码&#xff0c;因为代码量巨大&#xff0c;其中包括两个头文件&#xff0c;两个实现函数 &#xff0c; 这块就写一个两个栈实现按一个队列的头文件以及函数名&#xff0c;填补代码不难的&#xff0c;有需要的话&#xff0c;我在评论区发出来&#xff1a; 这部分…

堆排序(超详细的原理图以及代码注释)

在了解学习堆排序之前&#xff0c;我们必须清楚以下的概念&#xff1a; 上述概念搞清楚之后&#xff0c;来看下面的原理图&#xff1a; 然后原理部分就讲完了 下面来看如何由子节点下标推出父节点和父节点下标推出子节点下标 下面这个原理图来展现一下为什么在调整大顶堆的时候…

【C++】模拟实现多线程中的信号量

信号量&#xff1a; 二元信号量和一般信号量 二元信号量是最简单的一种锁&#xff0c;适合那种被唯一线程访问的资源&#xff0c;而一般信号量就允许多线程并发的访问资源。 二元信号量类似于互斥量&#xff0c;但是有一点不同的是&#xff0c;互斥量只能被上锁的那个线程释…

senfile函数实例的运行过程截图

//要传输的文件内容如下所示&#xff1a; 启动服务器&#xff0c;等待客户端连接&#xff08;在同一台主机上模拟的&#xff09; 客户端远程登录&#xff0c;这里是在本地登录 这个要注意一点就是远程登陆的时候一定要带上端口号不然连接失败&#xff01;&#xff01;转载于:ht…

【C++】单例模式(懒汉、饿汉)

1.什么是单例模式&#xff1f; 顾名思义&#xff0c;单例模式就是在这个单例类确保只有一个对象被创建。也就是说这个类只能实例化一个对象。 特征&#xff1a;1.单例类最多只能有一个实例&#xff1b; 2.单例类必须自己创建自己唯一的实例&#xff1b; 3.单例类必须给所有其…

【LeetCode】344、反转字符串

我们同学去深信服笔试时&#xff0c;考了这个算法代码&#xff0c;所给的要求是三分钟之内写出这个程序 题目如上所示&#xff0c;主流解题方法有三种&#xff1a; 1.这种方法应该算是普普通通的那种&#xff0c;左下标left&#xff0c;右下标right--&#xff0c;然后循环中使用…