目录
一.生产者消费者问题(问题描述)
有一个生产者在生产产品,这些产品将提供给一个消费者去消费,为了使生产者和消费者能并发执行,在两者之间设置一个有多个缓冲区的缓冲池,生产者将它生产的产品放入缓冲区中,消费者可以从缓冲区中取走产品进行消费,不允许消费者到一个空的缓冲区中取产品,也不允许生产者向一个已经装满产品且尚未被取走的缓冲区中投放产品。
二.问题分析
1.只有缓冲区没满时,生产者才能把产品放入缓冲区,否则必须等待;
2.只有缓冲区不为空时,消费者才能从中取出产品,否则必须等待;
3.在一个线程进行生产或消费时,其余线程不能进行生产或消费等操作,即保持线程间旳同步;
4.缓冲区是临界资源,各进程必须互斥的访问;
三.背景知识
本实验在一个进程中执行两个线程,一个是生产者线程,一个是消费者线程。生产者线程生产物品,然后将物品放置在一个空缓冲区中供消费者线程消费。消费者线程从缓冲区中获得物品,然后释放缓冲区。
本实验需要三个信号量:
1.互斥信号量mutex:实现进程对缓冲池的互斥使用。
2.信号量Empty:表示缓冲池中空缓冲区的数量。
3.信号量Full:表示缓冲池中满缓冲区的数量。
四.代码实现
生产者线程:
void producer(int index)
{
int n = 0;
std::unique_lock<std::mutex> lock(mtx); //unique_ptr获取互斥锁,类似于智能指针的智能锁
for (int i = 0; i < 100; i++)
{
while (q.Full())//如果缓冲区满了,生产者线程应该进入等待状态,并且把mtx互斥锁释放掉
{
cv.wait(lock);
}
q.Push(n); //缓冲区不满时,可以生产产品放入缓冲区
cout << "生产者生产:" << n << "号物品" << endl;
n++;
cv.notify_all();//通知其他所有的线程,其他线程得到该通知,就会从 等待状态 变成 阻塞状态 获取互斥锁后才能继续执行
}
}
消费者线程:
void consumer(int index)
{
std::unique_lock<std::mutex> lock(mtx);
for (int i = 0; i < 100; i++)
{
while (q.Empty())//如果缓冲区为空,消费者线程应该进入等待状态,并且把mtx互斥锁释放掉
{
cv.wait(lock);
}
int v = 0;
q.Pop(v);//如果缓冲区不为空,消费者可以消费缓冲区中的产品
cout << "消费者消费:" << v << "号物品" << endl;
cv.notify_all();
}
}
完整代码:
#include<iostream>
#include <mutex>
#include <condition_variable>//条件变量的头文件
#include <thread>
using namespace std;
std::mutex mtx; //定义互斥锁,做线程间的互斥操作
std::condition_variable cv; //定义条件变量,做线程间的同步通信操作
class Queue
{
public:
Queue()
{
data = NULL;
front = 0;
rear = 0;
size = 0;
maxsize = 10;//缓冲区的最大空间为10
data = new int[maxsize];
}
~Queue()
{
if (data != NULL)
{
delete[] data;
data = NULL;
}
}
void Push(const int& v) //生产产品
{
data[rear] = v;
rear = (rear + 1) % maxsize;
size =size+1;
}
void Pop(int& v) //消费产品
{
v = data[front];
front = (front + 1) % maxsize;
size=size-1;
}
int Size() const //缓冲区中现有元素个数
{
return size;
}
bool Empty() const //判断现在缓冲区是否为空
{
if (Size() == 0)
{
return true;
}
else
{
return false;
}
}
bool Full() const//判断现在缓冲区是否为满
{
if (Size() == maxsize)
{
return true;
}
else
{
return false;
}
}
private:
int* data; //指针指向循环队列连续空间
int front; //指向队头指针
int rear; //指向队尾指针
int size; //当前缓冲区中元素个数
int maxsize; //缓冲区的最大容纳空间
};
Queue q;
void producer(int index)
{
int n = 0;
std::unique_lock<std::mutex> lock(mtx); //unique_ptr获取互斥锁,类似于智能指针的智能锁
for (int i = 0; i < 100; i++)
{
while (q.Full())//如果缓冲区满了,生产者线程应该进入等待状态,并且把mtx互斥锁释放掉
{
cv.wait(lock);
}
q.Push(n); //缓冲区不满时,可以生产产品放入缓冲区
cout << "生产者生产:" << n << "号物品" << endl;
n++;
cv.notify_all();//通知其他所有的线程,其他线程得到该通知,就会从 等待状态 变成 阻塞状态 获取互斥锁后才能继续执行
}
}
void consumer(int index)
{
std::unique_lock<std::mutex> lock(mtx);
for (int i = 0; i < 100; i++)
{
while (q.Empty())//如果缓冲区为空,消费者线程应该进入等待状态,并且把mtx互斥锁释放掉
{
cv.wait(lock);
}
int v = 0;
q.Pop(v);//如果缓冲区不为空,消费者可以消费缓冲区中的产品
cout << "消费者消费:" << v << "号物品" << endl;
cv.notify_all();
}
}
int main()
{
std::thread t1(producer, 1); //生产者线程
std::thread t2(consumer, 2); //消费者线程
t1.join();
t2.join();
return 0;
}
部分运行结果展示:
五.实验结论
生产者消费者问题是一个经典的同步问题,通过本次实验我学到了线程同步问题以及信号量机制的使用,加深了我对操作系统多线程机制的理解和认识,同时也掌握了一些C++语言编程语言相关知识。