本人在实际项目中用到两种无锁队列,本人还是贴下代码
#ifndef __MPMC_BOUNDED_QUEUE_HPP__
#define __MPMC_BOUNDED_QUEUE_HPP__
#include <atomic>
template<typename T>
class mpmc_bounded_queue
{
public:
mpmc_bounded_queue(size_t buffer_size)
: buffer_(new cell_t[buffer_size])
, buffer_mask_(buffer_size - 1)
{
// assert((buffer_size >= 2) &&
// ((buffer_size & (buffer_size - 1)) == 0));
for (size_t i = 0; i != buffer_size; i += 1)
buffer_[i].sequence_.store(i, std::memory_order_relaxed);
enqueue_pos_.store(0, std::memory_order_relaxed);
dequeue_pos_.store(0, std::memory_order_relaxed);
deque_size = 0;
}
~mpmc_bounded_queue()
{
if (NULL != buffer_) {
delete[] buffer_;
}
}
bool enqueue(T const& data)
{
cell_t* cell;
size_t pos = enqueue_pos_.load(std::memory_order_relaxed);
for (;;)
{
cell = &buffer_[pos & buffer_mask_];
size_t seq =
cell->sequence_.load(std::memory_order_acquire);
intptr_t dif = (intptr_t)seq - (intptr_t)pos;
if (dif == 0)
{
if (enqueue_pos_.compare_exchange_weak
(pos, pos + 1, std::memory_order_relaxed))
break;
}
else if (dif < 0)
return false;
else
pos = enqueue_pos_.load(std::memory_order_relaxed);
}
cell->data_ = data;
cell->sequence_.store(pos + 1, std::memory_order_release);
deque_size++;
return true;
}
int size()
{
return deque_size;
}
bool dequeue(T& data)
{
cell_t* cell;
size_t pos = dequeue_pos_.load(std::memory_order_relaxed);
for (;;)
{
cell = &buffer_[pos & buffer_mask_];
size_t seq =
cell->sequence_.load(std::memory_order_acquire);
intptr_t dif = (intptr_t)seq - (intptr_t)(pos + 1);
if (dif == 0)
{
if (dequeue_pos_.compare_exchange_weak
(pos, pos + 1, std::memory_order_relaxed))
break;
}
else if (dif < 0)
return false;
else
pos = dequeue_pos_.load(std::memory_order_relaxed);
}
data = cell->data_;
cell->sequence_.store
(pos + buffer_mask_ + 1, std::memory_order_release);
deque_size--;
return true;
}
private:
struct cell_t
{
std::atomic<size_t> sequence_;
T data_;
};
static size_t const cacheline_size = 64;
typedef char cacheline_pad_t[cacheline_size];
cacheline_pad_t pad0_;
cell_t* const buffer_;
size_t const buffer_mask_;
cacheline_pad_t pad1_;
std::atomic<size_t> enqueue_pos_;
cacheline_pad_t pad2_;
std::atomic<size_t> dequeue_pos_;
cacheline_pad_t pad3_;
std::atomic_int deque_size;
mpmc_bounded_queue(mpmc_bounded_queue const&);
void operator = (mpmc_bounded_queue const&);
};
#endif
example:
typedef mpmc_bounded_queue<void *> lockless_queue_t;
lockless_queue_t * m_tx_queue[10];
m_tx_queue[i] = new lockless_queue_t(1024 * 1024 * 4);
//loop要退出必须要把该管理器的内存数据刷进去Mysql
while (true)
{
void* data = NULL;
bool bRet = m_tx_queue[i]->dequeue(data);
if(m_exit)
{ return true;}
}
bool bRet = m_tx_queue[iMode]->enqueue(pData);
还有另外一种无锁队列实现起来更加简单,但是只能针对一个线程读、另外一个线程写,但是基本上符合很多业务需求,但是这种也有固定缺点,第一无法动态伸缩。
#include <string.h>
#include "unlockqueue.h"
UnlockQueue::UnlockQueue()
:m_pBuffer(NULL)
,m_pHead(NULL)
,m_pTail(NULL)
,m_nSize(0)
{
}
UnlockQueue::UnlockQueue(size_t nSize)
:m_pBuffer(NULL)
,m_pHead(NULL)
,m_pTail(NULL)
,m_nSize(nSize)
{
Init(nSize);
}
UnlockQueue::~UnlockQueue()
{
UnInit();
}
bool UnlockQueue::Init(size_t nSize)
{
if(m_pBuffer != NULL)
{
delete [] m_pBuffer;
}
m_pBuffer = new char[nSize];
m_pHead = m_pBuffer;
m_pTail = m_pBuffer;
m_nSize = nSize;
return true;
}
void UnlockQueue::UnInit()
{
if(m_pBuffer != NULL)
{
delete [] m_pBuffer;
m_pBuffer = NULL;
m_pHead = NULL;
m_pTail = NULL;
}
}
bool UnlockQueue::Write(const char* pData, size_t nLen)
{
size_t nDist = (m_pTail + m_nSize - m_pHead);
size_t nUsed = nDist >= m_nSize ? nDist - m_nSize : nDist;
if(nLen + 1 + nUsed > m_nSize)
{
return false;
}
if(m_pTail + nLen >= m_pBuffer + m_nSize)
{
size_t nSeg1 = (size_t)(m_pBuffer + m_nSize - m_pTail);
size_t nSeg2 = nLen - nSeg1;
memcpy(m_pTail, pData, nSeg1);
memcpy(m_pBuffer, pData + nSeg1, nSeg2);
m_pTail = m_pBuffer + nSeg2;
}
else
{
memcpy(m_pTail, pData, nLen);
m_pTail += nLen;
}
return true;
}
bool UnlockQueue::Read(char* pBuf, size_t nLen)
{
size_t nDist = (m_pTail + m_nSize - m_pHead);
size_t nUsed = nDist >= m_nSize ? nDist - m_nSize : nDist;
if(nLen > nUsed)
{
return false;
}
if(m_pHead + nLen >= m_pBuffer + m_nSize)
{
size_t nSeg1 = (size_t)(m_pBuffer + m_nSize - m_pHead);
size_t nSeg2 = nLen - nSeg1;
memcpy(pBuf, m_pHead, nSeg1);
memcpy(pBuf + nSeg1, m_pBuffer, nSeg2);
m_pHead = m_pBuffer + nSeg2;
}
else
{
memcpy(pBuf, m_pHead, nLen);
m_pHead += nLen;
}
return true;
}
bool UnlockQueue::Discard(size_t nLen)
{
size_t nDist = (m_pTail + m_nSize - m_pHead);
size_t nUsed = nDist >= m_nSize ? nDist - m_nSize : nDist;
if(nLen > nUsed)
{
return false;
}
if(m_pHead + nLen >= m_pBuffer + m_nSize)
{
size_t nSeg1 = (size_t)(m_pBuffer + m_nSize - m_pHead);
size_t nSeg2 = nLen - nSeg1;
m_pHead = m_pBuffer + nSeg2;
}
else
{
m_pHead += nLen;
}
return true;
}