-------------------------------------------------------------------------------------------------------------------------
2021-06-23更新
基于生产者消费者模型,在多个消费者等待的过程中,notify会导致惊群效应,解决方案
producer -> consumerMananger 模型(单线程-单线程(管理多线程))
producer->通知consumerMananger去处理
consumerMananger->根据自定义机制(或者空闲状态),让单一的consumer去消费,此过程放在单线程中处理)
------------------------------------------------------------------------------------------------------------------------
0、前言
生产者消费者模式:一个producer线程生产,一个consumer消耗,当producer过快的时候实行丢帧操作,当consumer过快时实行超时机制的等待(等待不到就返回空)
通俗的话讲:当"面包"生产过剩的时候,就倒掉仓库中的部分面包(俗称:资本主义倒牛奶),当"面包"消耗的过快的时候,就让顾客等着,如果顾客没有耐心等到(超时机制),就让他回去
1、实现自定义事件(事件同步)
#pragma once
#include <windows.h>
#include <process.h>
#include <string>
typedef enum
{
EVENT_STATUS_FAIL = -1, // failed
EVENT_STATUS_SUCCESS = 0, // success
EVENT_STATUS_TRUE = 1, // result was true
EVENT_STATUS_TIMEOUT = -3, // time out
EVENT_STATUS_OPEN = -7, // Something failed to open
}EVENT_STATUS;
LPCWSTR stringToLpcwstr(const std::string& orig)
{
int len;
int slength = orig.length() + 1;
len = MultiByteToWideChar(CP_ACP, 0, orig.c_str(), slength, 0, 0);
wchar_t * buf = new wchar_t[len];
MultiByteToWideChar(CP_ACP, 0, orig.c_str(), slength, buf, len);
std::wstring r(buf);
delete[] buf;
buf = nullptr;
return r.c_str();
}
class CircularEvent
{
public:
CircularEvent(bool manualReset = true, const std::string& name = "")
: mSignaled(true)
, mName(stringToLpcwstr(name))
{
Init(manualReset);
}
~CircularEvent()
{
Relese();
}
public:
virtual EVENT_STATUS Signal()
{
if (!mEvent)
{
return EVENT_STATUS_OPEN;
}
if (SetEvent(mEvent))
{
mSignaled = true;
return EVENT_STATUS_SUCCESS;
}
return EVENT_STATUS_FAIL;
}
virtual EVENT_STATUS Clear()
{
if (!mEvent)
{
return EVENT_STATUS_OPEN;
}
if (ResetEvent(mEvent))
{
mSignaled = false;
return EVENT_STATUS_SUCCESS;
}
return EVENT_STATUS_FAIL;
}
virtual EVENT_STATUS SetState(bool signaled = true)
{
if (signaled)
{
return Signal();
}
return Clear();
}
virtual EVENT_STATUS GetState(bool* pSignaled)
{
if (!mEvent)
{
return EVENT_STATUS_OPEN;
}
DWORD word = WaitForSingleObject(mEvent, 0);
if (word == WAIT_TIMEOUT)
{
return EVENT_STATUS_FAIL;
}
else if (word == WAIT_OBJECT_0)
{
*pSignaled = true;
}
else
{
*pSignaled = false;
}
return EVENT_STATUS_SUCCESS;
}
virtual EVENT_STATUS SetManualReset(bool manualReset)
{
Relese();
Init(manualReset);
if (mEvent)
{
return EVENT_STATUS_SUCCESS;
}
return EVENT_STATUS_FAIL;
}
virtual EVENT_STATUS WaitForSignal(uint32_t timeout = 0xffffffff)
{
if (!mEvent)
{
return EVENT_STATUS_OPEN;
}
DWORD word = WaitForSingleObject(mEvent, timeout);
if (word == WAIT_TIMEOUT)
{
return EVENT_STATUS_TIMEOUT;
}
else if (word == WAIT_OBJECT_0)
{
return EVENT_STATUS_SUCCESS;
}
return EVENT_STATUS_FAIL;
}
private:
void Init(bool manualReset)
{
mEvent = CreateEvent(nullptr, manualReset, mSignaled, mName);
}
void Relese()
{
if (mEvent)
{
CloseHandle(mEvent);
mEvent = nullptr;
}
}
private:
HANDLE mEvent;
LPCWSTR mName;
bool mSignaled;
};
2、实现多线程同步的circularBuffer
#pragma once
#include <shared_mutex>
template <typename DatePtr>
class CircularBuffer
{
public:
CircularBuffer()
: mHead(0)
, mTail(0)
, mCircBufferCount(0)
, mFillIndex(0)
, mEmptyIndex(0)
, mAbortFlag(nullptr)
{
}
virtual ~CircularBuffer()
{
Clear();
}
inline void SetAbortFlag(const bool * pAbortFlag)
{
mAbortFlag = pAbortFlag;
}
inline unsigned int GetCircBufferCount(void) const
{
return mCircBufferCount;
}
inline bool IsEmpty(void) const
{
return GetCircBufferCount() == 0;
}
inline unsigned int GetNumFrames(void) const
{
return (unsigned int)mDates.size();
}
bool Add(DatePtr pInFrameData)
{
mDates.push_back(pInFrameData);
std::shared_mutex* lock = new std::shared_mutex;
mLocks.push_back(lock);
return (mDates.size() == mLocks.size() && lock);
}
DatePtr StartProduceNextBuffer(void)
{
while (1)
{
if (!WaitForLockOrAbort(&mDataBufferLock))
return NULL;
if (mCircBufferCount == mDates.size())
{
mDataBufferLock.Unlock();
if (!WaitForEventOrAbort(&mNotFullEvent))
return NULL;
continue;
}
break;
}
if (!WaitForLockOrAbort(mLocks[mHead])) return NULL;
mFillIndex = mHead;
mHead = (mHead + 1) % ((unsigned int)(mDates.size()));
mCircBufferCount++;
if (mCircBufferCount == mDates.size())
mNotFullEvent.SetState(false);
mDataBufferLock.Unlock();
return mDates[mFillIndex];
}
void EndProduceNextBuffer()
{
mLocks[mFillIndex]->Unlock();
mNotEmptyEvent.SetState(true);
}
DatePtr StartConsumeNextBuffer(void)
{
while (1)
{
if (!WaitForLockOrAbort(&mDataBufferLock))
return NULL;
if (mCircBufferCount == 0)
{
mDataBufferLock.Unlock();
if (!WaitForEventOrAbort(&mNotEmptyEvent))
return NULL;
continue;
}
break;
}
if (!WaitForLockOrAbort(mLocks[mTail]))
return NULL;
mEmptyIndex = mTail;
mTail = (mTail + 1) % ((unsigned int)mDates.size());
mCircBufferCount--;
if (mCircBufferCount == 0)
mNotEmptyEvent.SetState(false);
mDataBufferLock.Unlock();
return mDates[mEmptyIndex];
}
void EndConsumeNextBuffer()
{
mLocks[mEmptyIndex]->Unlock();
mNotFullEvent.SetState(true);
}
void Clear(void)
{
for (std::vector<std::shared_mutex*>::iterator iter(mLocks.begin()); iter != mLocks.end(); ++iter)
delete *iter;
mLocks.clear();
mDates.clear();
mHead = mTail = mFillIndex = mEmptyIndex = mCircBufferCount = 0;
mAbortFlag = NULL;
}
private:
bool WaitForEventOrAbort(CircularEvent *event)
{
do
{
int status = event->WaitForSignal(timeout);
if (status == EVENT_STATUS_TIMEOUT)
{
if (mAbortFlag)
{
if (*mAbortFlag)
{
return false;
}
}
}
if (status == EVENT_STATUS_FAIL)
{
return false;
}
if (status == EVENT_STATUS_SUCCESS)
{
break;
}
} while (true);
return true;
}
bool WaitForLockOrAbort(std::shared_mutex * mutex)
{
do
{
bool status = mutex->try_lock();
if (status)
{
break;
}
else
{
if (mAbortFlag)
{
if (*mAbortFlag)
{
return false;
}
}
}
}
while (true);
return true;
}
private:
std::vector <std::shared_mutex*> mLocks;
std::vector <DatePtr> mDates;
unsigned int mHead; // 首指针
unsigned int mTail; // 尾指针
unsigned int mCircBufferCount; // buffer 个数
CircularEvent mNotEmptyEvent; // 空到不空
CircularEvent mNotFullEvent; // 饱和到不饱和
std::shared_mutex mDataBufferLock;
unsigned int mFillIndex; // 添加帧的顺序
unsigned int mEmptyIndex; // 删除帧的index
const bool * mAbortFlag; // 指定中断线程
};
4、丢弃操作和放入操作由外部线程调用(用户根据自定义完成)