(CircularBuffer) c++环形缓冲区

本说明涉及如下内容

        1. 什么是环形缓冲区

        2. 如何实现环形缓冲区

什么是环形缓冲区

环形缓冲区是一个先进先出(FIFO)的闭环的存储空间,可等效为首尾相连的队列。可类比为在内存中规划了一个水轮车(环形缓冲区),将水装载到轮子一侧的格子(将数据保存在缓冲区内存内),通过轮子的转动便可实现水(数据)在不同的水道(线程)内进行传输,从而实现数据在线程间的零拷贝传输。

        ps. 因进程间会有各自独立的堆区,所以环形缓冲区无法实现进程间通讯。

如何实现环形缓冲区

一. 定义类成员变量

        1. 为了方便项目使用,本套环形缓冲区采用泛型设计,定义待处理数据类型为DATA

template <typename DATA>

        2.  定义保存数据的数组

std::vector<DATA> data;

        3. 因为环形缓冲区在不同的线程间运作,故需要设计线程管理

/**
 * @brief 读锁
*/
std::mutex m_read_lock;

/**
 * @brief 写锁
*/
std::mutex m_write_lock;

/**
 * @brief 用于避免空数据处理的条件变量
*/
std::condition_variable m_cv;

        4. 定义缓冲区大小变量

/**
 * @brief 缓冲区最大尺寸
*/
int max_size;


/**
 * @brief 缓冲区现存数据量
*/
int count;

        5. 定义缓冲区读写头

/**
 * @brief 读头
*/
int head;

/**
 * @brief 写头
*/
int end;

        6. 定义缓冲区工作状态标识

bool is_working = false;

二. 定义缓冲区数据情况函数

/**
 * @brief determine whether the buffer is empty
 *
 * @return true  : no data in buffer
 * @return false : buffer has data
 */
bool is_empty()
{
    if (this->count == 0)
    {
        return true;
    }
    return false;
}

/**
 * @brief is the buffer full
 *
 * @return true  : full
 * @return false : dissatisfaction
 */
bool is_full()
{
    if (this->count == this->max_size)
    {
        return true;
    }
    return false;
}

三. 定义缓冲区构造函数

/**
 * @brief Construct a new Circular Buffer object
 *
 * @param size : buffer size
 */
CircularBuffer(int size)
{
    // 对缓冲区进行上锁,避免误操作
    std::unique_lock<std::mutex> lock_w(this->m_write_lock);
    std::unique_lock<std::mutex> lock_r(this->m_read_lock);

    // 设置缓冲区尺寸
    if (size <= 0)
    {
        Log_ERROR << "Buffer size must be greater than 0" << Log_END;
    }
    else
    {
        // 缓冲区预分配内存(通过此操作,避免std::vector重新分配内存,导致性能降低)
        this->data.reserve(size);

        // 正式为缓冲区分配内存
        for (int i = 0; i < size; i++)
        {
            DATA process;
            this->data.emplace_back(process);
        }

        // 缓冲区各成员变量初始化
        this->max_size = size;
        this->count = 0;
        this->head = 0;
        this->end = 0;
        this->is_working = true;
    }
}

四. 定义缓冲区数据量处理函数

/**
 * @brief Get the Data Size object
 * 
 * @return int 
 */
int getDataSize()
{
    std::unique_lock<std::mutex> lock_w(this->m_write_lock);
    std::unique_lock<std::mutex> lock_r(this->m_read_lock);

    if (this->is_working == false)
    {
        return -1;
    }

    return this->count;
}

/**
 * @brief clear buffer
 * 
 * @return true  : clear successfully
 * @return false : empty failed
 */
bool clearBuffer()
{
    std::unique_lock<std::mutex> lock_w(this->m_write_lock);
    std::unique_lock<std::mutex> lock_r(this->m_read_lock);

    if (this->is_working == false)
    {
        Log_ERROR << "the buffer has not started working" << Log_END;
        return false;
    }

    this->count = 0;
    this->head = 0;
    this->end = 0;

    return true;
}

/**
 * @brief Get the Max size object
 * 
 * @return int 
 */
int getMax_size()
{
    return this->max_size;
}

五. 数据插入函数

/**
 * @brief insert data into the buffer
 * 
 * @param data   : data to be added
 * @return true  : successfully added
 * @return false : add failed
 */
bool insert(DATA data)
{
    if (this->is_working == false)
    {
        return false;
    }

    std::unique_lock<std::mutex> lock(this->m_write_lock);

    // 等待缓冲区有剩余空间,没有则在这里进行阻塞
    this->m_cv.wait(lock, [this]
                    { return this->is_full() == false; });

    try
    {
        this->data[this->end] = data;
        this->end = (this->end + 1) % this->max_size;
        ++this->count;
    }
    catch (const std::exception &e)
    {
        Log_ERROR << e.what() << Log_END;
    }

    lock.unlock();
    this->m_cv.notify_one();

    return true;
}

六. 数据弹出函数

/**
 * @brief eject data from buffer
 * 
 * @param data   : container for receiving pop up data
 * @return true  : eject successfully
 * @return false : eject failed
 */
bool eject(DATA &data)
{
    if (this->is_working == false)
    {
        return false;
    }

    std::unique_lock<std::mutex> lock(this->m_read_lock);

    this->m_cv.wait(lock, [this]
                    { return this->is_empty() == false; });

    try
    {
        data = this->data[this->head];
        this->head = (this->head + 1) % this->max_size;
        --this->count;
    }
    catch (const std::exception &e)
    {
        Log_ERROR << e.what() << Log_END;
    }

    lock.unlock();
    this->m_cv.notify_one();
    return true;
}

完整代码

#pragma once

/**
 * @file CircularBuffer.hpp
 * @author jiyilin 2474803745@qq.com
 * @brief this is a program for creating and using multithreaded circular buffer
 * @version 0.1
 * @date 2023-07-26
 *
 * @copyright Copyright (c) 2023
 *
 */

#include "log_system/LoggingSystem.h"
#include <condition_variable>
#include <mutex>
#include <vector>
#include <chrono>
#include <thread>

namespace Memory
{
    /**
     * @brief this class is used to use ring buffers
     *
     * @tparam DATA data
     *
     * @memberof data              : ring buffer for saving data
     * @memberof m_read_lock       : read lock
     * @memberof m_write_lock      : write lock
     *
     * @memberof m_cv              : condition variable to determine whether reading and writing can continue
     *
     * @memberof max_size          : maximum value
     * @memberof count             : existing data volume
     * @memberof head              : buffer read header
     * @memberof end               : buffer write header
     * @memberof is_working        : identification of whether the buffer works normally
     */
    template <typename DATA>
    class CircularBuffer
    {
    private:
        std::vector<DATA> data;

        std::mutex m_read_lock;
        std::mutex m_write_lock;

        std::condition_variable m_cv;

        int max_size;

        int count;

        int head;

        int end;

        bool is_working = false;

    private:
        /**
         * @brief determine whether the buffer is empty
         *
         * @return true  : no data in buffer
         * @return false : buffer has data
         */
        bool is_empty()
        {
            if (this->count == 0)
            {
                return true;
            }
            return false;
        }

        /**
         * @brief is the buffer full
         *
         * @return true  : full
         * @return false : dissatisfaction
         */
        bool is_full()
        {
            if (this->count == this->max_size)
            {
                return true;
            }
            return false;
        }

    public:
        /**
         * @brief Construct a new Circular Buffer object
         *
         * @param size : buffer size
         */
        CircularBuffer(int size)
        {
            std::unique_lock<std::mutex> lock_w(this->m_write_lock);
            std::unique_lock<std::mutex> lock_r(this->m_read_lock);

            if (size <= 0)
            {
                Log_ERROR << "Buffer size must be greater than 0" << Log_END;
            }
            else
            {
                this->data.reserve(size);
                for (int i = 0; i < size; i++)
                {
                    DATA process;
                    this->data.emplace_back(process);
                }

                this->max_size = size;
                this->count = 0;
                this->head = 0;
                this->end = 0;
                this->is_working = true;
            }
        }

        /**
         * @brief Get the Data Size object
         * 
         * @return int 
         */
        int getDataSize()
        {
            std::unique_lock<std::mutex> lock_w(this->m_write_lock);
            std::unique_lock<std::mutex> lock_r(this->m_read_lock);

            if (this->is_working == false)
            {
                return -1;
            }

            return this->count;
        }

        /**
         * @brief clear buffer
         * 
         * @return true  : clear successfully
         * @return false : empty failed
         */
        bool clearBuffer()
        {
            std::unique_lock<std::mutex> lock_w(this->m_write_lock);
            std::unique_lock<std::mutex> lock_r(this->m_read_lock);

            if (this->is_working == false)
            {
                Log_ERROR << "the buffer has not started working" << Log_END;
                return false;
            }

            this->count = 0;
            this->head = 0;
            this->end = 0;

            return true;
        }

        /**
         * @brief Get the Max size object
         * 
         * @return int 
         */
        int getMax_size()
        {
            return this->max_size;
        }

        /**
         * @brief insert data into the buffer
         * 
         * @param data   : data to be added
         * @return true  : successfully added
         * @return false : add failed
         */
        bool insert(DATA data)
        {
            std::unique_lock<std::mutex> lock(this->m_write_lock);

            if (this->is_working == false)
            {
                return false;
            }

            this->m_cv.wait(lock, [this]
                            { return this->is_full() == false; });

            try
            {
                this->data[this->end] = data;
                this->end = (this->end + 1) % this->max_size;
                ++this->count;
            }
            catch (const std::exception &e)
            {
                Log_ERROR << e.what() << Log_END;
            }

            lock.unlock();
            this->m_cv.notify_one();

            return true;
        }

        /**
         * @brief eject data from buffer
         * 
         * @param data   : container for receiving pop up data
         * @return true  : eject successfully
         * @return false : eject failed
         */
        bool eject(DATA &data)
        {
            std::unique_lock<std::mutex> lock(this->m_read_lock);

            if (this->is_working == false)
            {
                return false;
            }

            this->m_cv.wait(lock, [this]
                            { return this->is_empty() == false; });

            try
            {
                data = this->data[this->head];
                this->head = (this->head + 1) % this->max_size;
                --this->count;
            }
            catch (const std::exception &e)
            {
                Log_ERROR << e.what() << Log_END;
            }

            lock.unlock();
            this->m_cv.notify_one();
            return true;
        }

        /**
         * @brief Destroy the Circular Buffer object
         * 
         */
        ~CircularBuffer(){};
    };
} // namespace Memory

调用demo

# include "log_system/LoggingSystem.h"
# include "CircularBuffer/CircularBuffer.hpp"

void WriteThread(Memory::CircularBuffer<int *> *buffer)
{
    for (int i = 0; i < 100; i++)
    {
        int *process = new int;
        *process = i;
        buffer->insert(process);
    }
}

void TestThread(Memory::CircularBuffer<int *> *buffer)
{
    while (true)
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
        if (buffer->getDataSize() != 0)
        {
            std::cout << "buffer size = " << buffer->getDataSize() << std::endl;
        }
        
    }
}

int main()
{
    Memory::CircularBuffer<int *> buffer(10);

    std::thread writeThreader(WriteThread, &buffer);

    std::thread testThreader(TestThread, &buffer);

    int *output;
    while (true)
    {
        if (buffer.eject(output) == true)
        {
            std::cout << *output << std::endl;
        }
    }

    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值