生产者消费者问题
1.实验内容
(1)了解经典同步问题“生产者和消费者”
生产者与消费者可以通过一个环形缓冲池联系起来,环形缓冲池由几个大小相等的缓冲块组成,每个缓冲块容纳一个产品。每个生产者可不断地每次往缓冲池中送一个生产产品,而每个消费者则可不断地每次从缓冲池中取出一个产品。指针i和指针i分别指出当前的第一个空缓冲块和第一个满缓冲块。
(2)分析和理解
(1)既存在合作同步问题,也存在临界区互斥问题
合作同步:当缓冲池全满时,表示供过于求,生产者必须等待,同时唤醒 消费者;当缓冲池全空时,表示供不应求,消费者应等待,同时唤醒生产者。
互斥:缓冲池显然是临界资源,所在生产者与消费都要使用它,而且都要改变它的状态。
(2)基于环形缓冲区的生产者与消费者关系形式描述:公用信号量mutex:初值为1,用于实现临界区互斥生产者私用信号量empty:初值为n指示空缓冲块数目消费者私用信号量full:初值为0指示满缓冲块数目
整型量i和i初值为0i指示首空缓冲块序号,i指示首满缓冲块序号
2.背景知识
本实验要求设计在同一个进程地址空间内执行的两个线程。生产者线程生产物品,然后将物品放置在一个空缓冲区中供消费者线程消费。消费者线程从缓冲区中获得物品,然后释放缓冲区。
生产者线程生产物品时,若无空缓冲区可用,生产者线程必须等待消费者线程释放出一个空缓冲区;消费者线程消费物品时,若缓冲区为空,消费者线程将被阻塞,直到新的物品被生产出来。
本实验要求设计并实现一个进程,该进程拥有一个生产者线程和一个消费者线程,它们使用N个不同的缓冲区(N为一个确定的数值,例如N=32)。需要使用如下信号量:
一个互斥信号量,用以阻止生产者线程和消费者线程同时操作缓冲区列表;
一个信号量,当生产者线程生产出一个物品时可以用它向消费者线程发出信号;
一个信号量,消费者线程释放出一个空缓冲区时可以用它向生产者线程发出信号;
3.思路
我们使用队列(先进先出表)Queue来表示上述具有n个缓冲区的缓冲池。每投入(取出)一个产品,缓存池中就相应的插入(删除)一个节点。
此外,我们引入一个整型变量num,置其初值为0,当生产者(消费者)进程向缓冲池投入(取走)一个产品时,num对应的加一(减一)。
同时,由于缓冲区是共享的,因此需要对生产者、消费者使用缓冲区进行限制,以此达到同步的效果,即在生产者向缓冲区投入产品时,消费者不得使用缓冲池;消费者向缓冲区取出产品时同理。
4.核心代码
#include<iostream>
#include<random>
#include <time.h>
#include<thread>
#include <Windows.h>
#include<mutex>
using namespace std;
class buffer
{
public:
int cap[4];//定义缓冲区大小为4
int empty;//记录空的位置数量
int full;//记录被占用的位置数量
mutex lo;//互斥信号量
void into()//初始化
{
empty = 5;
full = 0;
}
void setdata(int data)
{
while (1) {
while (!empty) {}//防止缓冲区为满的时候放入
empty -= 1;//空位数量减一
lo.lock();//上锁
cout << "生产的数为:" << data << endl;
cap[full] = data;//将产品放入缓冲区最后一个位置
display();
lo.unlock();//解锁
full += 1;//占用位数量加一
break;
}
}
void takeout()
{
int x;
while (1)
{
while (!full) {}//防止缓冲区为空的时候取出
full -= 1;//占用位数量减一
lo.lock();//上锁
x = cap[full];//从缓冲区取出产品
cout << "消费的数为:" << x << endl;
display();
lo.unlock();//解锁
empty += 1;//空位数量加一
break;
}
}
void display()//显示缓冲区中的数据
{
cout << "缓冲区:";
if (full == 0)
cout << "空" << endl;
else
{
for (int i = 0; i < full; i++)
cout << cap[i] << " ";
cout << endl;
}
cout << endl;
}
};
void producer(buffer* p)
{
int s;
uniform_int_distribution<unsigned> k(500, 1500);//设置随机数区间
static uniform_int_distribution<unsigned> u(1, 100);//设置随机数区间
static default_random_engine e(time(0));//以时间作为种子生成随机数
for (int i = 0; i < 20; i++)//生产20次
{
s = u(e);
p->setdata(s);
Sleep(k(e));
}
}
//消费者线程
void consumer(buffer* p) {
uniform_int_distribution<unsigned> k(1000, 2000);//设置随机数区间
default_random_engine v(time(0));//以时间作为种子生成随机数
for (int i = 0; i < 20; i++) //消费20次
{
p->takeout();
Sleep(k(v));
}
}
int main() {
buffer p;
p.into();//初始化缓冲区
thread pro(producer, &p);//创建生产者子线程
thread con(consumer, &p);//创建消费者子线程
pro.join();//阻塞主线程
con.join();//阻塞主线程
cout << "结束!" << endl;
return 0;
}
结果:
结论:
生产者/消费者问题可以描述为:两个或者更多的进程(线程)共享同一个缓冲区,其中一个或多个进程(线程)作为“生产者”会不断地向缓冲区中添加数据,另一个或者多个进程(线程)作为“消费者”从缓冲区中取走数据。生产者/消费者模型关注的是以下几点:n 生产者和消费者必须互斥的使用缓冲区n 缓冲区空时,消费者不能读取数据n 缓冲区满时,生产者不能添加数据。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)