ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

linux C++无锁RingBuffer实现

linux C++无锁RingBuffer实现 C++ 无锁环形缓冲区实现详解引言本文详细展示了一个完整的C++ 无锁环形缓冲区实现,包含日志模块、环形缓冲区核心实现以及测试程序。该实现支持单生产者单消费者(SPSC)和多生产者单消费者(MPSC)两种模式,并提供了阻塞式(满时等待)和覆盖式(满时覆盖旧数据)两种缓冲区策略。项目结构log_print.hpp/log_print.cpp:多线程安全的日志打印模块ringbuff.hpp:无锁环形缓冲区核心实现main.cpp:测试程序,验证各种缓冲区模式的功能和正确性核心特性无锁设计: 使用原子操作和内存序保证线程安全高性能: 避免锁竞争,提高并发性能灵活配置: 支持SPSC和MPSC模式,支持阻塞和覆盖策略线程安全日志: 内置日志模块便于调试多线程环境以下是完整的实现代码:log_print.hpp#includeiostream// 日志打印模块头文件// 提供多线程安全的日志打印功能,支持不同日志级别(INFO/WARNING/ERROR)// 自动捕获文件名、行号和线程ID,便于调试多线程环境#includesstream#includethread#includestring#includesys/syscall.h#includeunistd.henumLogLevel{INFO,WARNING,ERROR};// 多线程日志安全打印类classLogPrint{public:LogPrint(std::string fileName,intlineNumber,LogLevel logLevel);~LogPrint();// 重载 运算符,支持链式调用,接收cout一样的参数LogPrintoperator(constautomessage){ssmessage;return*this;}private:std::stringstream ss;};// 文件名和行号,只显示文件名,不显示路径#defineLOG_PRINT(logLevel)LogPrint(std::string(__FILE__).substr(std::string(__FILE__).find_last_of('/')+1),__LINE__,logLevel)log_print.cpp#includeiostream// 日志打印模块实现文件// 实现LogPrint类的构造函数和析构函数// 构造函数根据日志级别设置前缀颜色,析构函数输出完整的日志信息#includestring#includesstream#includesys/syscall.h#includeunistd.h#include"log_print.hpp"LogPrint::LogPrint(std::string fileName,intlineNumber,LogLevel logLevel){switch(logLevel){caseINFO:ss"[INFO] ";// 显示绿色信息,设置颜色为绿色break;caseWARNING:ss"[WARNING] ";// 显示黄色警告break;caseERROR:ss"[ERROR] ";// 显示红色错误break;}// 线程显示tid,方便调试多线程环境ssfileName":"lineNumber" "syscall(SYS_gettid)" : ";}LogPrint::~LogPrint(){std::coutss.str()std::endl;};ringbuff.hpp/* 实现一个单生产者单消费者无锁环形缓冲区 */#includeiostream#includethread#includeatomic#includevector#includestring#includeutility// For std::move#includethread// For std::this_thread::yield#includecstddef// For size_t#include"log_print.hpp"// For LOG_PRINTclassItem{public:// "sink" 参数:按值传递,调用方可以选择拷贝或移动Item(size_t c,std::string v):count(c),value(std::move(v)){}Item():count(0),value(""){}size_t count;std::string value;};templatetypenameTclassRingBufferBase{public:virtual~RingBufferBase()=default;virtualboolpush(constTvalue)=0;virtualboolisLoop()=0;virtualboolpop(Tvalue)=0;virtualboolisEmpty()=0;// 检查数值是否是2的幂次方staticboolisPowerOfTwo(size_t n){returnn!=0(n(n-1))==0;}virtualintgetOverwriteCount(){return0;}protected:// 构造函数放在 protected,只允许派生类调用explicitRingBufferBase(size_t buffer_size):buffer_size_(buffer_size),mask_(buffer_size-1),buffer(buffer_size){if(!isPowerOfTwo(buffer_size)){throwstd::invalid_argument("buffer_size must be a power of 2");}this-head.store(0,std::memory_order_relaxed);this-tail.store(0,std::memory_order_relaxed);}std::vectorTbuffer;size_t buffer_size_;size_t mask_;// buffer_size_ - 1, 用于取模(仅当 buffer_size_ 是 2 的幂次方时有效)alignas(64)std::atomicsize_thead;// 消费者真正可读的head索引(head_commit),是已经准备好数据的索引(MPSC中还有一个head是预留的slot,尚未填充数据,消费者不可读)alignas(64)std::atomicsize_ttail;// head/tail 分离缓存行,避免伪共享};// 单生产者单消费者无锁环形缓冲区模板基类templatetypenameTclassSpscRingBuffer:publicRingBufferBaseT{public:virtual~SpscRingBuffer()=default;// 消费者:从缓冲区弹出元素(push/pop 逻辑通用,pop 不变,push 由子类实现不同策略)boolpop(Tvalue){// WithLoop 模式下生产者也会写 tail,因此这里必须用 acquire,// 确保能看到生产者 release 写的最新 tail(防止读到过期值导致多读/乱序)size_t current_tail=this-isLoop()?this-tail.load(std::memory_order_acquire):this-tail.load(std::memory_order_relaxed);size_t current_head=this-head.load(std::memory_order_acquire);// acquire 确保能看到生产者 release 写的最新 head// 空检测:head == tail(索引不取模,直接用差值判断)if(current_head-current_tail==0){LOG_PRINT(INFO
返回列表