生產者消費者模型四(多-多模型)



生產者——消費者問題(producer-consumer),又名:有界緩衝區(bounded-buffer)問題
多生產者——多消費者

設計要點:
 當緩衝區滿已滿,而此時生產者還想向緩衝區中放入一個新的數據項時。則讓生產者睡眠,待消費者從緩衝區中取出一個或多個數據項時再喚醒生產者。
 同時,多個生產者,則需要多添加一個條件變量和計數變量,用來保證多個生產者之間對消息隊列的互斥訪問

 當緩衝區滿已空,而此時消費者還想從緩衝區中取出一個新的數據項時。則讓消費者睡眠,待生產者向緩衝區中放入一個或多個數據項時再喚醒消費者。
 同時,多個消費者,則需要多添加一個條件變量和計數變量,用來保證多個消費者之間對消息隊列的互斥訪問

代碼實現如下:(IDE : VS2017)

#include"stdafx.h"
#include<string>
#include<stdio.h>
#include <windows.h>//Sleep()函數需要包含的頭文件
#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 item_counter_mtx_for_producer;//互斥量,保證多個生產者對緩衝區的互斥訪問
 size_t item_counter_for_producer; //生產者計數變量

 std::mutex item_counter_mtx_for_consumer;//互斥量,保證多個消費者對緩衝區的互斥訪問
 size_t item_counter_for_consumer; //消費者計數變量

 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);
 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); // 生產者等待"產品庫緩衝區不爲滿"這一條件發生.
 }

 (ir->item_buffer)[ir->write_position] = item; // 寫入產品.
 (ir->write_position)++; // 寫入位置後移.
 std::cout << "Producer thread: " << std::this_thread::get_id() << "is producing the:" << item << "^th item..." << std::endl;
 (gItemRepository.item_counter_for_producer)++;

 if (ir->write_position == kItemRepositorySize) // 寫入位置若是在隊列最後則重新設置爲初始位置.
  ir->write_position = 0;

 (ir->repo_not_empty).notify_all(); // 通知消費者產品庫不爲空.
 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); // 消費者等待"產品庫緩衝區不爲空"這一條件發生.
 }

 data = (ir->item_buffer)[ir->read_position]; // 讀取某一產品
 (ir->read_position)++; // 讀取位置後移

 std::cout << "Consumer thread " << std::this_thread::get_id()
  << " is consuming the " << data << "^th item" << std::endl;

 if (ir->read_position >= kItemRepositorySize) // 讀取位置若移到最後,則重新置位.
  ir->read_position = 0;

 (ir->repo_not_full).notify_all(); // 通知生產者產品庫不爲滿.
 lock.unlock(); // 解鎖.

 return data; // 返回產品.
}


void ProducerTask() // 生產者任務
{
 bool ready_to_exit = false;
 while (1)
 {
  Sleep(1);
  std::unique_lock<std::mutex>lock(gItemRepository.item_counter_mtx_for_producer);

  if (gItemRepository.item_counter_for_producer<kItemsToProduce)
  {
   ProduceItem(&gItemRepository, gItemRepository.item_counter_for_producer);
  }
  else ready_to_exit = true;

  lock.unlock();

  if (ready_to_exit == true) {
   break;
  }
 }
}


//多個消費者會對緩衝區進行讀寫,所以需要特殊處理
void ConsumerTask() // 消費者任務
{
 bool ready_to_exit = false;
 while (1)
 {
  Sleep(1);
  std::unique_lock<std::mutex> lock(gItemRepository.item_counter_mtx_for_consumer);
  if (gItemRepository.item_counter_for_consumer < kItemsToProduce)
  {
   int item = ConsumeItem(&gItemRepository); // 消費一個產品.
   ++(gItemRepository.item_counter_for_consumer);
  }
  else ready_to_exit = true;

  lock.unlock();

  if (ready_to_exit == true) break;
 }
}

void InitItemRepository(ItemRepository *ir)
{
 ir->write_position = 0; // 初始化產品寫入位置.
 ir->read_position = 0;  // 初始化產品讀取位置.
 ir->item_counter_for_consumer = 0;   // 初始化計數位置.
 ir->item_counter_for_producer = 0;   // 初始化計數位置.
}

int main()
{
 //ItemRepository gItemRepository;
 InitItemRepository(&gItemRepository);

 std::thread producer1(ProducerTask); // 創建生產者線程.
 std::thread producer2(ProducerTask);// 創建消費之線程.
 std::thread producer3(ProducerTask);
 std::thread consumer1(ConsumerTask);
 std::thread consumer2(ConsumerTask);
 std::thread consumer3(ConsumerTask);
 producer1.join();
 producer2.join();
 producer3.join();
 consumer1.join();
 consumer2.join();
 consumer3.join();
 system("pause");
 return 0;

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章