生產者——消費者問題(producer-consumer),又名:有界緩衝區(bounded-buffer)問題
本文介紹單生產者——單消費者的代碼實現
設計要點:
當緩衝區滿已滿,而此時生產者還想向緩衝區中放入一個新的數據項時。則讓生產者睡眠,待消費者從緩衝區中取出一個或多個數據項時再喚醒生產者。
同樣的,當緩衝區滿已空,而此時消費者還想從緩衝區中取出一個新的數據項時。則讓消費者睡眠,待生產者向緩衝區中放入一個或多個數據項時再喚醒消費者。
代碼實現如下:(IDE : VS2017)
#include"stdafx.h"
#include<string>
#include<stdio.h>
#include <stdlib.h>
#include<thread>
#include <mutex>
#include<iostream>
using namespace std;
static const int kItemRepositorySize = 10; // Item buffer size.
static const int kItemsToProduce = 100; // How many items we plan to produce.
class ItemRepository
{
public:
int item_buffer[kItemRepositorySize]; // 產品緩衝區, 配合 read_position 和 write_position 模型環形隊列.
size_t read_position; // 消費者讀取產品位置.
size_t write_position; // 生產者寫入產品位置.
std::mutex mtx; // 互斥量,保護產品緩衝區
std::condition_variable repo_not_full; // 條件變量, 指示產品緩衝區不爲滿.
std::condition_variable repo_not_empty; // 條件變量, 指示產品緩衝區不爲空.
}gItemRepository; // 產品庫全局變量, 生產者和消費者操作該變量.
void ProduceItem(ItemRepository *ir, int item)
{
std::unique_lock<std::mutex> lock(ir->mtx);
//初始位置都是 0,當兩者相差一個位置時,定義滿狀態。(最多存儲kItemRepositorySize - 1個)
while (((ir->write_position+1) % kItemRepositorySize)== ir->read_position)
{ // item buffer is full, just wait here.
std::cout << "Producer is waiting for an repo_not_full notification...\n";
(ir->repo_not_full).wait(lock); // 生產者等待"產品庫緩衝區不爲滿"這一條件發生.
}
std::cout << "Producer is producting the " << item << "^th item..." << std::endl;
(ir->item_buffer)[ir->write_position] = item; // 寫入產品.
(ir->write_position)++; // 寫入位置後移.
if (ir->write_position == kItemRepositorySize) // 寫入位置若是在隊列最後則重新設置爲初始位置.
ir->write_position = 0;
(ir->repo_not_empty).notify_all(); // 通知消費者產品庫不爲空.
//條件變量的wait 會自動調用鎖的unlock ,此處註釋掉效果一樣
lock.unlock(); // 解鎖.
}
int ConsumeItem(ItemRepository *ir)
{
int data;
std::unique_lock<std::mutex> lock(ir->mtx);
// item buffer is empty, just wait here.
while (ir->write_position == ir->read_position)
{
std::cout << "Consumer is waiting for an repo_not_empty notification...\n";
(ir->repo_not_empty).wait(lock); // 消費者等待"產品庫緩衝區不爲空"這一條件發生.
}
std::cout << "Consumer is consuming the " << ir->read_position << "^th item" << std::endl;
data = (ir->item_buffer)[ir->read_position]; // 讀取某一產品
(ir->read_position)++; // 讀取位置後移
if (ir->read_position >= kItemRepositorySize) // 讀取位置若移到最後,則重新置位.
ir->read_position = 0;
(ir->repo_not_full).notify_all(); // 通知生產者產品庫不爲滿.
//條件變量的wait 會自動調用鎖的unlock ,此處註釋掉效果一樣
lock.unlock(); // 解鎖.
return data; // 返回產品.
}
void ProducerTask() // 生產者任務
{
for (int i = 0; i < kItemsToProduce; ++i)
{
ProduceItem(&gItemRepository, i); // 循環生產 kItemsToProduce 個產品.
}
}
void ConsumerTask() // 消費者任務
{
static int cnt = 0;
while (1)
{
//sleep(1);
int item = ConsumeItem(&gItemRepository); // 消費一個產品.
if (++cnt == kItemsToProduce) break; // 如果產品消費個數爲 kItemsToProduce, 則退出.
}
}
void InitItemRepository(ItemRepository *ir)
{
ir->write_position = 0; // 初始化產品寫入位置.
ir->read_position = 0; // 初始化產品讀取位置.
}
int main()
{
ItemRepository gItemRepository;
InitItemRepository(&gItemRepository);
std::thread producer(ProducerTask); // 創建生產者線程.
std::thread consumer(ConsumerTask); // 創建消費之線程.
producer.join();
consumer.join();
system("pause");
return 0;
}