前言
SPSCQueue(Single Producer Single Consumer Queue,单生产者单消费者队列)是一个单生产者单消费者的线程安全队列,适应于一个线程生产(PUSH)、一个线程消费(POP),相对于多个生产者、多个消费者的队列实现更简单。
实现原理
1、使用环形缓冲数组来保存数据,申明头指针、尾指针来标识PUSH\POP操作的位置
2、使用C++原子锁来保证线程安全(而非进程锁,避免多线程访问时的竞态条件)
代码实现
#include <cstdint>
#include <atomic>
//----------------------------------------------------------------
// 适用于单个生产者单个消费者
// 注:单个生产者,多个消费者也可以使用,但是消费者pop不是必成功
//----------------------------------------------------------------
template<typename T, int32_t N>
class ScspQueue
{
public:
void clear();
bool empty();
bool full();
int32_t size();
bool push(const T&);
bool pop(T&);
private:
int32_t m_readIndex = 0;
int32_t m_writeIndex = 0;
std::atomic_bool m_readLock = 0;
std::atomic_bool m_writeLock = 0;
T m_aData[N + 1];
};
template<typename T, int32_t N>
void ScspQueue<T, N>::clear()
{
bool expected = true;
while (m_readLock.compare_exchange_strong(expected, false));
expected = true;
while (m_writeLock.compare_exchange_strong(expected, false));
m_readIndex = m_writeIndex = 0;
m_readIndex = 0;
m_writeIndex = 0;
}
template<typename T, int32_t N>
bool ScspQueue<T, N>::empty()
{
return size() == 0;
}
template<typename T, int32_t N>
bool ScspQueue<T, N>::full()
{
return size() == N;
}
template<typename T, int32_t N>
int32_t ScspQueue<T, N>::size()
{
return (m_writeIndex - m_readIndex + N + 1) % (N + 1);
}
template<typename T, int32_t N>
bool ScspQueue<T, N>::push(const T& data)
{
if (full()){
return false;
}
memcpy(&m_aData[m_writeIndex], &data, sizeof(T));
bool expected = true;
if (!m_writeLock.compare_exchange_strong(expected, false)) {
return false;
}
m_writeIndex = (m_writeIndex + +1 + N + 1) % (N + 1);
m_writeLock = false;
return true;
}
template<typename T, int32_t N>
bool ScspQueue<T, N>::pop(T& data)
{
if (empty()){
return false;
}
memcpy(&data, &m_aData[m_readIndex], sizeof(T));
bool expected = true;
if (!m_readLock.compare_exchange_strong(expected, false)) {
return false;
}
m_readIndex = (m_readIndex + 1 + N + 1) % (N + 1);
m_readIndex = 0;
return true;
}