背景
之前写了一篇基于单生产者单消费者的无锁队列,链接如下
https://blog.csdn.net/lab1228/article/details/127357286
在多生产者多消费者的场景下,常规操作是对单个队列加锁,但是这样锁竞争会导致性能变差,于是基于双队列,分拆生产者和消费者队列,这样消费者和生产者之间就不会出现锁竞争,提升性能。
代码实现
#include <iostream>
#include <list>
#include <condition_variable>
//1.内部有两个list,生产者把消息放到生产队列,消费者从消费队列取消息
//2.使用了两把锁分别管理两个队列
//3.使用了两个条件变量分别管理生产者和消费者的等待唤醒
//4.队列可以有block / nonblock两种状态
//5.如果为block,则生产者队列最大长度为maxQueueSize,
//6.如果为nonblock,则不限制最大长度
template<class T>
class MsgQueue
{
public:
MsgQueue(bool isNoBlock = true, uint32_t maxQueueSize = 1024):
_isNoBlock(isNoBlock), _maxQueueSize(maxQueueSize)
{
}
~MsgQueue() {
}
//如果为BLOCK模式,队列满的情况下会一直阻塞至队列交换后,才能完成
int put(const T data)
{
std::unique_lock<std::mutex> lock(_mtxPut);
if (_isNoBlock)
{
_listPut->emplace_back(std::move(data));
}
else
{
//判断是否满,满则阻塞
while (_maxQueueSize <= _listPut->size())
{
_cvPut.wait(lock);
}
_listPut->emplace_back(std::move(data));
_cvGet.notify_one();
}
return 0;
}
int get(T& data)
{
std::unique_lock<std::mutex> lock(_mtxGet);
if (!_listGet->empty())
{
data = _listGet->front();
_listGet->pop_front();
}
else
{
if (swap() > 0)
{
data = _listGet->front();
_listGet->pop_front();
}
else {
return -1;
}
}
return 0;
}
private:
int swapList()
{
return _listGet.size();
}
int swap()
{
std::unique_lock<std::mutex> lock(_mtxPut);
if (_isNoBlock)
{
std::list<T>* listTmp = _listGet;
_listGet = _listPut;
_listPut = listTmp;
return _listGet->size();
}
while (_listPut->empty())
{
_cvGet.wait(lock);
}
std::list<T>* listTmp = _listGet;
_listGet = _listPut;
_listPut = listTmp;
_cvPut.notify_one();
return _listGet->size();
}
std::list<T> _list1;
std::list<T> _list2;
//指向当前可用于消费的队列
std::list<T>* _listGet = &_list1;
//指向当前可用于生产的队列
std::list<T>* _listPut = &_list2;
//生产者队列最大长度
uint32_t _maxQueueSize = 5;
bool _isNoBlock = true;
std::mutex _mtxPut;
std::mutex _mtxGet;
std::condition_variable _cvPut;
std::condition_variable _cvGet;
};