生产者-消费者模型



我正在尝试模拟关于多线程的生产者-消费者模型。

我们假设有三条规则需要遵守:

  1. 当桶中装满产品时,生产者不能将产品添加到桶中
  2. 当水桶为空时,消费者无法从水桶中获取产品
  3. 生产和消费不可能同时发生。换句话说,这两个操作是异步的

现在我有了:

  1. 一个int变量,可用于存储存储桶中产品的数量
  2. 用于存储桶容量的const int变量,在我的代码中其值为5
  3. 互斥对象的int变量,其初始值为1
  4. 用于挂起句柄的vector<HANDLE>变量,并且将有一个函数调度这些挂起的线程

结果是:有时它工作得很好,但有时它会变成死锁。代码和结果如下:

代码:

#include <iostream>
#include <Windows.h>
#include <vector>
using namespace std;
// the count of product, the initial value is 0
int product_count = 0;
const int product_capacity = 5;
int mutex = 1;
vector<HANDLE> suspendedHandleVector;
HANDLE GetCurrentRealHandle() {
HANDLE realHandle = 0;
return OpenThread(THREAD_ALL_ACCESS, TRUE, GetCurrentThreadId());
}
void ThreadScheduling() {
if (suspendedHandleVector.size() > 0) {
HANDLE handle = suspendedHandleVector[0];
suspendedHandleVector.erase(suspendedHandleVector.begin());
ResumeThread(handle);
}
}
void P() {
--mutex;
if (mutex < 0) {
auto handle = GetCurrentRealHandle();
suspendedHandleVector.push_back(handle);
SuspendThread(handle);
}
}
void V() {
++mutex;
if (mutex >= 0) {
ThreadScheduling();
}
}
DWORD WINAPI ProducerThread(LPVOID param) {
while (true) {
P();
if (product_count == product_capacity) {
V();
continue;
}
++product_count;
cout << "I'm producer, and there are " << product_count << " products now" << endl;
V();
Sleep(100);
}
return 0;
}
DWORD WINAPI ConsumerThread(LPVOID param) {
while (true) {
P();
if (product_count == 0) {
V();
continue;
}
--product_count;
cout << "I'm consumer, and there are " << product_count << " products rest now" << endl;
V();
Sleep(150);
}
return 0;
}
void main() {
auto producer_handle = CreateThread(nullptr, 0, ProducerThread, nullptr, 0, nullptr);
auto consumer_handle = CreateThread(nullptr, 0, ConsumerThread, nullptr, 0, nullptr);
while (true) {
cout << suspendedHandleVector.size() << endl; // This is for debugging
Sleep(100);
}
}

按预期工作时的结果:

0
I'm producer, and there are 1 products now
I'm consumer, and there are 0 products rest now
0
I'm producer, and there are 1 products now
I'm consumer, and there are 0 products rest now
0
I'm producer, and there are 1 products now
0
I'm consumer, and there are 0 products rest now
I'm producer, and there are 1 products now
0
I'm producer, and there are 2 products now
I'm consumer, and there are 1 products rest now

这是一个预期的无限循环。产品数量将在4和5之间流动,因为我设置生产者比消费者多睡一点时间

但意外的结果是:

I'm producer, and there are 5 products now
I'm consumer, and there are 4 products rest now
0
I'm consumer, and there are 4 products rest now
I'm producer, and there are 5 products now
0
0
I'm consumer, and there are 4 products rest now
I'm producer, and there are 5 products now
0
2
2
2
2
2
2

正如我们所看到的,挂起的线程向量的大小从0达到2,其中省略了1。

这只是两个线程同时被唤醒的巧合吗

我更认为我的代码有问题,需要您的帮助

在测试过程中,我还遇到了一个问题:如何获取线程,然后将其存储在向量中。

我不确定是否使用OpenThread来执行此操作。我的课程要求我使用系统调用,所以我没有包括thread头文件。

谢谢你的帮助!

在生产者-消费者问题中有三个参与者:生产者、消费者和用于存储生成数据的缓冲区。生产者和消费者通常生活在一个或多个线程中(多生产者单消费者单生产者多消费者多生产者多消费者(。

现在,这种模式被广泛使用,因为它是避免显式同步的方法之一。

正如我所说,消费者和消费者通常生活在一个或多个线程中。缓冲区通常是一个同步队列(既可以是锁定队列,也可以是无锁定队列(。

一个很短的片段:


SyncronizedQueue queue;
void consumer()
{
while(1) {
queue.push(std::rand());
}
}
void producer()
{
while(1)
{
auto product = queue.pop();   // blocks until there are elements in the 
}
}
void main()
{
std::thread producerThread{producer};
std::thread consumerThread{consumer};
producerThread.join();
consumerThread.join();
}

因此,任务是写入队列以确保push元素是安全的,并在推送新元素时解锁pop

一种简单的实现方法是使用条件变量,这样pop函数就会被阻塞,直到触发一个条件,push方法向队列中添加一个元素并触发该条件。

最新更新