看見網上給的生產者消費者的例子是基於windows API下的編程,不但脫離了操作系統底層設計的初衷,還更依賴於平臺。
故使用C++13標準,重寫了生產者消費者模擬程序。
首先,由於C++沒有信號量機制,利用互斥鎖實現一個信號量類。代碼如下:
【Semaphore.h】
/*
一個信號量類(來自網絡)
*/
#pragma once
#include<mutex>
class Semaphore
{
public:
Semaphore(unsigned long count = 0) : m_count(count) {}
Semaphore(const Semaphore&) = delete;
Semaphore& operator=(const Semaphore&) = delete;
void Signal(){
{
std::unique_lock<std::mutex> lock(m_mutex);
++m_count;
}
m_cv_uptr.notify_one();
}
void Wait(){
std::unique_lock<std::mutex> lock(m_mutex);
while (m_count == 0) { // 這裏可能會虛假喚醒
m_cv_uptr.wait(lock);
}
--m_count;
}
private:
std::mutex m_mutex;
std::condition_variable m_cv_uptr;
unsigned long m_count;
};
其次,定義一個BufferSimulation類,來實現緩衝區的模擬
【BufferSimulation.h】
/*
使用C++11標準
寫了一個消費者生產者——緩衝區類
作者:呂翔宇
E-mail:[email protected]
2019年5月26日
版權所有 ALL RIGHTS RESERVED!
*/
#pragma warning(disable:4996)
#pragma once
#include<iostream>
#include<vector>
#include<string>
#include<mutex>
#include<thread>
#include"Semaphore.h"
class BufferSimulation{
public:
BufferSimulation(int producerNum, int consumerNum, int bufferSize, int delayTime, bool stepMode);
~BufferSimulation();
private:
std::vector<std::string> buffer; // 緩衝區
std::vector<std::thread> producers; // 生產者們
std::vector<std::thread> consumers; // 消費者們
int producerNum, consumerNum, bufferSize, delayTime;
std::mutex t_mutex; // 用於線程間的互斥
Semaphore bufferSemaphore; // 緩衝區信號量
int produce_ptr; // 生產者放置位置
int consume_ptr; // 消費者使用位置
bool stop;
int count = 0; // 記錄當前貨物編號
std::thread stepModeThread; // 步過進程
public:
void printBuffer(int loc);
private:
void produce(int id); // 生產程序
void consume(int id); // 消費程序
void stepMode(); // 單步模式
public:
bool canStop;
};
其實現代碼如下
【BufferSimulation.cpp】
/*
使用C++11標準
寫了一個消費者生產者——緩衝區類
作者:呂翔宇
E-mail:[email protected]
2019年5月26日
版權所有 ALL RIGHTS RESERVED!
*/
#include "BufferSimulation.h"
BufferSimulation::BufferSimulation(int producerNum, int consumerNum, int bufferSize, int delayTime, bool stepMode) :
producerNum(producerNum),
consumerNum(consumerNum),
bufferSize(bufferSize),
delayTime(delayTime),
stop(false),
bufferSemaphore(bufferSize), // 信號量與緩衝區大小一致
buffer(bufferSize),
canStop(false)
{
if (stepMode)
stepModeThread = std::thread(&BufferSimulation::stepMode, this);
else
canStop = true;
for (int i = 0; i < producerNum; i++) {
producers.push_back(std::thread(&BufferSimulation::produce, this,i));
std::cout << "創建了一個生產者者進程!" <<"進程號爲:"<< producers[i].get_id()<< std::endl;
}
// 創建消費者
for (int j = 0; j < consumerNum; j++) {
consumers.push_back(std::thread(&BufferSimulation::consume, this,j));
std::cout << "創建了一個消費者進程!" << "進程號爲:" << consumers[j].get_id() << std::endl;
}
}
void BufferSimulation::stepMode() {
canStop = false;
bool pass = true;
while (true) {
_sleep(300);
if (pass) {
t_mutex.lock();
pass = false;
}
std::cout << "\n指令 1.停止運行 2.調到下一秒 3.打印緩衝區\n請輸入:";
int tmp;
std::cin >> tmp;
if (tmp == 1) {
stop = true;
t_mutex.unlock();
_sleep(delayTime);
canStop = true;
return;
}else {
if (tmp == 2) {
t_mutex.unlock();
_sleep(1000);
pass = true;
}
else {
if (tmp == 3) {
printBuffer(-1);
}
else {
std::cout << "指令無效\n";
}
}
}
}
}
BufferSimulation::~BufferSimulation()
{
stop = true;
for (int i = 0; i < producerNum; i++) {
producers[i].detach();
}
for (int i = 0; i < consumerNum; i++) {
std::cout << "銷燬消費者" << std::endl;
consumers[i].detach();
}
system("pause");
}
void BufferSimulation::printBuffer(int loc) {
std::cout << "————————————" << std::endl;
std::cout << "緩衝區號\t\t內容\n";
for (int i = 0; i < bufferSize; i++) {
if (i != loc) {
std::cout << i << "\t\t\t" << buffer[i] << "\n";
}
else {
std::cout << i << "\t\t\t" << buffer[i] << "\t<--"<<"\n";
}
}
std::cout << "————————————" << std::endl;
}
void BufferSimulation::produce(int id) {
while (true) {
if (stop) {
std::cout << "\n生產者 " << id << " 退出\n";
break;
}
_sleep(delayTime);
bufferSemaphore.Wait(); // 等待有地方放
t_mutex.lock();
if (stop) {
bufferSemaphore.Signal();
t_mutex.unlock();
std::cout << "\n生產者 " << id << " 退出\n";
break;
}
buffer[produce_ptr] = std::to_string(count);
printBuffer(produce_ptr);
produce_ptr = (produce_ptr + 1) % bufferSize;
std::cout << std::this_thread::get_id()<<" "<< id << "號進程(生產者)生產:" << count << std::endl;
count++;
t_mutex.unlock();
}
}
void BufferSimulation::consume(int id) {
while (true) {
if (stop) {
std::cout << "\n消費者 " << id << " 退出\n";
break;
}
_sleep(delayTime);
bufferSemaphore.Signal(); // 等待有東西用
t_mutex.lock();
if (stop) {
bufferSemaphore.Wait();
t_mutex.unlock();
std::cout << "\n消費者 " << id << " 退出\n";
break;
}
std::string product = buffer[consume_ptr];
buffer[consume_ptr] = "";
printBuffer(consume_ptr);
consume_ptr = (consume_ptr + 1) % bufferSize;
std::cout << std::this_thread::get_id() << " " << id << "號進程(消費者)消費:" << product << std::endl;
t_mutex.unlock();
}
}
【main.cpp】
/*
基於C++11模擬生產者-消費者,緩衝隊列的實現
作者:呂翔宇
E-Mail:[email protected]
2019年5月26日
版權所有 ALL RIGHTS RESERVED!
*/
#include"BufferSimulation.h"
int main(int argc, char ** argv) {
int producerNum; int consumerNum; int bufferSize; int delayTime; bool stepMode;
std::cout << "生產者數:";
std::cin >> producerNum;
std::cout << "消費者數:";
std::cin >> consumerNum;
std::cout << "緩衝區大小:";
std::cin >> bufferSize;
std::cout << "間隔大小(毫秒):";
std::cin >> delayTime;
std::cout << "是否步過模式(0:自動運行,1:步過模式):";
std::cin >> stepMode;
BufferSimulation simulation(producerNum, consumerNum, bufferSize, delayTime, stepMode);
while (!simulation.canStop) _sleep(1000);
system("pause");
return 0;
}
下面在時間間隔模式下進行演示:
參數如下:
打印緩衝區得到:
停止運行:
自動模式下的演示略過,動態的不易演示。