自定義條件變量類:
#pragma once
/// <summary>
/// 用C++11實現跨平臺的條件等待類
/// </summary>
#include<functional>
#include<condition_variable>
#include <mutex>
class LanWaitCondition
{
public:
LanWaitCondition();
~LanWaitCondition();
/// <summary>
/// 等待
/// </summary>
void Wait();
/// <summary>
/// 喚醒一個
/// </summary>
void NotifyOne();
/// <summary>
/// 喚醒所有
/// </summary>
void NotifyAll();
private:
std::mutex m_mutex; //鎖
std::condition_variable m_cv; //阻塞等待條件
int m_waitCount = 0; //等待次數
int m_wakeupCount = 0; //喚醒次數
};
#include "LanWaitCondition.h"
LanWaitCondition::LanWaitCondition()
{
}
LanWaitCondition::~LanWaitCondition()
{
}
void LanWaitCondition::Wait()
{
std::unique_lock<std::mutex> lock(m_mutex);
m_cv.wait(lock);
}
void LanWaitCondition::NotifyOne()
{
m_cv.notify_one();
}
void LanWaitCondition::NotifyAll()
{
m_cv.notify_all();
}
生產者與消費者模型及其測試代碼
// C++11_ConditionWait.cpp : 此文件包含 "main" 函數。程序執行將在此處開始並結束。
//
#include <iostream>
#include <queue>
#include "LanWaitCondition.h"
#include <thread>
#include <chrono>
#include <atomic>
const int con_bufSize = 100000; //所謂的時間換空間!!! 100M緩存
const int con_perMsgSize = 1024;
char buf[con_bufSize][con_perMsgSize] = { 0 };
std::queue<int> queueForFree; //當前空閒隊列
LanWaitCondition lwcForFree; //空閒隊列條件變量
std::mutex mutexForFree; //空閒隊列鎖
std::queue<int> queueForUse; //已使用隊列
LanWaitCondition lwcForUse; //已使用隊列條件變量
std::mutex mutexForUse; //使用隊列鎖
const int con_productorSize = 3; //生產者數量
const int con_consumerSize = 8; //消費者數量
std::vector<std::thread*> vecTD; //具體業務的線程
bool bExit[con_productorSize + con_consumerSize]; //線程退出標誌 必須要讓每個線程自己把控這個變量!!!
//LanWaitCondition lwcForExit; //退出條件變量
std::atomic<int> produceMsgCount = 0;
std::atomic<int> consumerMsgCount = 0;
void Stop()
{
printf("bExit 爲 true %d!!!!!!!!!!!!!!!!\n", sizeof(bExit) / sizeof(bExit[0]));
//設置退出標誌爲true
for (int i = 0; i < sizeof(bExit) / sizeof(bExit[0]); i++)
{
bExit[i] = true;
}
//清空隊列
std::queue<int> tempQueue_1;
mutexForFree.lock();
queueForFree.swap(tempQueue_1);
mutexForFree.unlock();
//清空隊列
std::queue<int> tempQueue_2;
mutexForUse.lock();
queueForUse.swap(tempQueue_2);
mutexForUse.unlock();
//通知隊列退出
lwcForFree.NotifyAll();
for (int i = 0; i < con_productorSize; i++)
{
//確保線程一定退出!!
while (bExit[i])
{
std::chrono::milliseconds durable(60);
std::this_thread::sleep_for(durable);
lwcForFree.NotifyAll();
}
}
lwcForUse.NotifyAll();
for (int i = con_productorSize; i < vecTD.size(); i++)
{
//確保線程一定退出!!
while (bExit[i])
{
std::chrono::milliseconds durable(60);
std::this_thread::sleep_for(durable);
lwcForUse.NotifyAll();
}
}
//爲了讓子線程先退出,主線程再退出!!
//for (int i = 0; i < vecTD.size(); i++)
//{
// vecTD[i].join();//這種方式等待退出,在高併發的情況下,有可能會永遠阻塞在這裏!!因爲有些線程可能沒有被真正喚醒!!
//}
if (vecTD.empty() == false)
{
for (int i = 0; i < vecTD.size(); i++)
{
if (vecTD[i]->joinable())
{
vecTD[i]->join(); //確保delete時,該線程的狀態爲不能加入類型!!!若delete時線程是可加入的,則會引起崩潰!!!
}
delete vecTD[i];
}
vecTD.clear();
}
}
void TestProductor(int id, bool& bThreadExit)
{
//生產者生產字符
while (!bThreadExit)
{
#if 0
char tem[con_perMsgSize] = { 0 };
std::cin >> tem;
#else
//std::chrono::seconds durable(1);
//std::this_thread::sleep_for(durable);
std::string str = "6";
char tem[con_perMsgSize] = {0};
memcpy(tem, str.c_str(), sizeof(char) * str.length());
#endif
str = tem;
mutexForFree.lock();
if (queueForFree.size() <= 0)
{
mutexForFree.unlock();
if (bThreadExit)
{
break;
}
lwcForFree.Wait();
continue;
}
int idx = queueForFree.front();
queueForFree.pop();
mutexForFree.unlock();
memcpy(buf[idx], tem, con_perMsgSize);
//printf("生產者%d生產消息:%s\n", id, tem);
produceMsgCount++;
if (!bThreadExit)
{
mutexForUse.lock();
queueForUse.push(idx);
mutexForUse.unlock();
lwcForUse.NotifyOne();
}
}
bThreadExit = false; //表明自己退出了!!
printf("生產者%d退出!!\n", id);
}
void TestConsumer(int id, bool& bThreadExit)
{
//消費者消費字符
while (!bThreadExit)
{
bool bLoop = true;
while (bLoop)
{
mutexForUse.lock();
if (queueForUse.size() <= 0)
{
mutexForUse.unlock();
bLoop = false;
continue;
}
int idx = queueForUse.front();
queueForUse.pop();
mutexForUse.unlock();
char temp[con_perMsgSize] = { 0 };
memcpy(temp, buf[idx], con_perMsgSize);
//printf("消費者%d消費消息:%s\n", id, temp);
consumerMsgCount++;
if (bThreadExit)
{
break;
}
else
{
mutexForFree.lock();
queueForFree.push(idx);
mutexForFree.unlock();
lwcForFree.NotifyOne();
}
}
//如果是退出
if (bThreadExit)
{
break;
}
//printf("消費者%d Wait!!\n", id);
//必須放在後面!
lwcForUse.Wait();
//printf("消費者%d 跳出Wait!!\n", id);
}
bThreadExit = false; //表明自己退出了!!
printf("消費者%d退出!!\n", id);
}
void InputExit()
{
std::chrono::seconds durable(3);
std::this_thread::sleep_for(durable);
//lwcForExit.NotifyOne();
}
void Test()
{
for (int i = 0; i < con_bufSize; i++)
{
queueForFree.push(i);
}
for (int i = 0; i < con_productorSize; i++)
{
bExit[i] = false;
vecTD.emplace_back(new std::thread(TestProductor, i, std::ref(bExit[i]))); //聲明線程時開始,這個線程就開始執行了!!! //std::ref表示“傳引用”!!
}
for (int i = 0; i < con_consumerSize; i++)
{
bExit[con_productorSize + i] = false;
vecTD.emplace_back(new std::thread(TestConsumer, i, std::ref(bExit[con_productorSize + i]))); //聲明線程時開始,這個線程就開始執行了!!!
}
//std::thread stopTd(InputExit);
//stopTd.join();
//等待指示退出
//lwcForExit.Wait();
//退出
InputExit();
Stop();
//打印生產消息總數和消費消息總數
int tmpPMCount = produceMsgCount;
int tmpCMCount = consumerMsgCount;
printf("生產消息總數:%d 消費消息總數:%d\n", tmpPMCount, tmpCMCount);
}
int main()
{
for (int i = 0; i < 100; i++)
{
Test();
produceMsgCount = 0;
consumerMsgCount = 0;
printf("這是第%d次計算 \n", i + 1);
}
std::cout << "Hello World!\n";
}
生成結果:
100M緩存,3個生產者10個消費者,程序運行6s
debug版本,生產了87萬多個消息,消費了87萬多消息!!!
release版本,生產了855萬多個消息,消費了855萬多消息!!!
需要注意的是: 使用join方式,等待線程退出,在高併發的場景下有可能導致主線程永遠阻塞着,如下圖所示:
總線程數是24個,還有好幾個消費者線程沒有退出!!
所以,停止所有線程時,需要着重注意,具體請看“Stop”函數的實現!!!