环形队列

【简介】

ring buffer / circular buffer 又名环形队列 / 环形缓冲区,其通过开辟固定尺寸的内存来实现反复复用同一块内存的目的。由于预先开辟了固定尺寸的内容,所以当数据满的时候,可以有两种处理方式,具体使用哪一种按照实际需求,具体如下:

1)当队列满的时候,新来的数据会覆盖最古老的数据,这种数据结构的特点是数据的写入不会因为队列满了而停止,同时也会导致旧数据的丢失,常用在一些对老旧数据不敏感的场景。如果数据很重要且不希望被丢弃,那么不要使用这种覆盖的模式,比如 流媒体场景下,每一帧数据都要确保完整地被渲染出来,不然会出现跳帧和音画同步无法完成等问题,所以不适合这种模式。

2)当数据满的时候,block 或者禁止写入操作,直到队列有空间时再允许写入。这种模式下可以保证数据不被覆盖掉。

 

【实现】

boost库直接有现成的,参考 VS2015编译并配置boost 64位 - 夕西行 - 博客园 (cnblogs.com)

也可以自己实现,思路参考:

 [common c/c++] ring buffer/circular buffer 环形队列/环形缓冲区_c++ ringbuffer-CSDN博客

多线程环形缓冲区 - IAmAProgrammer - 博客园 (cnblogs.com)

 

 

环形队列本质上是一个定长的数组,两个指针在数组上指来指去。满足FIFO(先进先出)原则,在逻辑上是环形的(队列满时,队尾指针指向头的上一位)。参考自 详解环形队列_简述环形队列的优缺点-CSDN博客

 简单封装

#include <array>

template<typename T,const unsigned int N>

class RingQueue
{
public:
    RingQueue():head(0),tail(0){}

private:
    std::array<T, N> data;
    unsigned int head;
    unsigned int tail;

public:
    bool push(const T& item)    //入队
    {
        if (!full())
        {
            tail = (tail + 1) % N;
            data[tail] = item;
            return true;
        }
        return false;
    }

    bool peek(T& item)    //查看队首元素
    {
        if (!empty())
        {
            item = data[(head + 1) % N];
            return true;
        }
        return false;
    }

    bool pop()    //出队,弹出队首元素
    {
        if (!empty())
        {
            head = (head + 1) % N;
            return true;
        }
        return false;
    }

    bool full() const    //是否满
    {
        return (tail + 1) % N == head ? true : false;
    }

    bool empty() const    //是否空
    {
        return head == tail ? true : false;
    }

    unsigned int size() const    //元素个数
    {
        return (tail - head + N) % N;
    }
};

#include <iostream>
int main()
{
    RingQueue<std::pair<unsigned char, unsigned long long>, 5> rq;
    rq.push(std::make_pair(255, 12345));
    rq.push(std::make_pair(10, 54321));

    std::pair<unsigned char, unsigned long long> temp;
    while (rq.peek(temp))
    {
        std::cout << (int)temp.first << " " << temp.second << "\n";
        std::cout << rq.size() << "\n";
        rq.pop();
    }
    std::cout << rq.size() << "\n";
    return 0;
}

一个消费者、一个生产者案例 C++多线程的生产者消费者问题: Queue(队列)和环形缓存(RingBuffer)的效率对比-CSDN博客 

多生产者、多消费者案例 详解C++高性能无锁队列的原理与实现 - 知乎 (zhihu.com)

队列满时,阻塞与非阻塞版本 超越99%的程序员,chatgpt用C++写个线程安全无锁环形队列 - 知乎 (zhihu.com)

 

【模仿写的环形队列】

使用了原子变量+内存屏障

Ringbuffer: 单生产者、单消费者的无锁环形队列 (gitee.com)

参考:

【低时延】无锁环形缓冲lock-free ringbuff_c++ lockfreeringbuffer-CSDN博客

atomic原子编程中的Memory Order - 可可西 - 博客园 (cnblogs.com)

posted @ 2024-04-26 19:17  夕西行  阅读(127)  评论(0编辑  收藏  举报