讲师中心 微信公众号

工具推荐

视频效率加速
Seko
Seko

AI短剧一键生成成片

立刻MV
立刻MV

低成本歌曲 MV、视频生成

Laper
Laper

剧本策划到成片协作

剧云
剧云

中文短剧剧本制作平台

C++如何实现环形缓冲区?(无锁队列基础结构)

千辰吖_5210

千辰吖_5210

发布时间:2026-02-26 08:14:02

|

571人浏览过

|

来源于php中文网

原创

不能用 std::queue 做环形缓冲区,因其底层 std::deque 内存不连续、无原子读写指针接口,无法满足固定大小、连续内存、独立指针推进和 wrap-around 等核心要求。

c++如何实现环形缓冲区?(无锁队列基础结构)

为什么不用 std::queue 做环形缓冲区

因为 std::queue 底层默认用 std::deque,内存不连续,没法靠指针加减实现真正的“环形”;而且它没提供读写位置的原子访问接口,无法支撑无锁逻辑。环形缓冲区核心诉求是:固定大小、内存连续、读写指针可独立推进、支持 wrap-around(越界回绕)。所以得自己管内存和指针算术。

常见错误现象:std::queue::size() 在多线程里不可靠;用 std::vector 动态扩容会破坏地址稳定性;手动 new char[N] 但没对齐,导致原子操作失败(尤其在 ARM 或 x86-64 的 std::atomic<uintptr_t></uintptr_t> 上)。

  • 必须用 alignas(64) 对齐缓冲区(主流 CPU 缓存行宽度)
  • 容量必须是 2 的幂(方便用位运算代替取模,避免分支和除法)
  • 读写指针类型建议用 std::atomic<size_t></size_t>,别用 int 或裸指针

怎么写一个基础的单生产者单消费者(SPSC)环形缓冲区

SPSC 是唯一能完全无锁(lock-free)且无需内存屏障的场景——因为读写各只有一方,只需保证指针更新的原子性 + 数据写入的顺序可见性。关键不是“锁不锁”,而是“谁能看到谁的写”。

典型使用场景:日志采集线程往缓冲区塞数据,主线程定时刷盘;传感器采样中断服务程序写,用户态线程读。

立即学习“C++免费学习笔记(深入)”;

C++14
C++14

C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。

下载
  • 缓冲区大小设为 N(2 的幂),则有效容量是 N - 1(留 1 空位区分满/空)
  • 写指针推进后,先写数据,再更新指针(否则读者可能读到未初始化内存)
  • 读指针推进前,先读数据,再更新指针(否则写者可能覆盖未读数据)
  • 用 (index & (N - 1)) 替代 index % N,前提是 N 是 2 的幂

示例片段(简化版):

class SPSCRingBuffer {
    alignas(64) char buf_[1024];
    std::atomic<size_t> write_idx_{0};
    std::atomic<size_t> read_idx_{0};
public:
    bool try_push(const char* data, size_t len) {
        size_t w = write_idx_.load(std::memory_order_relaxed);
        size_t r = read_idx_.load(std::memory_order_acquire);
        if ((w - r) >= 1023) return false; // 满
        memcpy(buf_ + (w & 1023), data, len);
        write_idx_.store(w + 1, std::memory_order_release);
        return true;
    }
};

多生产者或多消费者时,为什么不能直接加锁

加锁本身不违反“无锁队列基础结构”的定位,但一旦加锁,就失去了 lock-free 的核心优势:任意线程崩溃不会阻塞其他线程。更现实的问题是性能瓶颈——比如多个采集线程争抢同一个 std::mutex,吞吐量卡死在几十万 ops/s,而无锁 SPSC 轻松破千万。

真正难的是 MPSC(多生产单消费)或 MPMC(多对多):需要解决 ABA 问题、指针更新竞争、内存重排干扰。这时候不能只靠 std::atomic::compare_exchange_weak,还得配合适当的 memory order 和 padding 防伪共享(false sharing)。

  • MPSC 至少要两个写指针字段:write_idx(全局)和 local_write_idx(每个生产者私有),避免写指针成为热点
  • 所有原子操作尽量用 std::memory_order_acquire/release,别图省事全用 relaxed
  • 读写指针变量之间至少隔 64 字节(用 alignas(64) 或填充数组),否则不同线程改不同指针也会因缓存行冲突拖慢

容易被忽略的边界:字节序、对齐、调试验证

环形缓冲区出问题,90% 不是逻辑错,而是底层细节失控。比如在嵌入式平台用 uint8_t* 直接 cast 成 int32_t* 读写,结果因未对齐触发硬件异常;或者调试时用 gdb 打印 read_idx_ 值,却发现它“卡住不动”,其实是优化让变量被寄存器缓存,没刷新到内存。

  • 所有跨线程访问的变量,必须声明为 std::atomic,哪怕只是 size_t —— 普通变量的读写在 C++ 标准里不保证原子性
  • 缓冲区起始地址必须满足目标类型对齐要求(例如存 double 就要 8 字节对齐),否则 memcpy 可能变慢,甚至 std::atomic<t></t> 构造失败
  • 验证是否真无锁:用 perf record -e instructions:u 看热点是否集中在 cmpxchg 指令,而不是 futex_wait

最麻烦的从来不是怎么写完,而是怎么确认它在所有负载下都既正确又不退化。比如批量写入时忘了检查剩余空间分段处理,一次 push 超过缓冲区剩余容量,就会静默丢数据——这种 bug 很难复现,也很难测出来。

热门AI工具

更多
火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

SoundView
SoundView Hot

SoundView是一款面向跨境电商的视频翻译、配音和本地化 AI 创作平台。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

SkildArt
SkildArt Hot

SkildArt是一款AI文本写作工具,一站式 AI 视觉创作平台。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

Flowith
Flowith Hot

Flowith是一款基于画布和智能体工作流的 AI 协作工作空间。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

7539

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

3585

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

4728

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

3145

2025.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

4728

2025.08.29

C++中int、float和double的区别
C++中int、float和double的区别

本专题整合了c++中int和double的区别,阅读专题下面的文章了解更多详细内容。

904

2025.10.23

C++ 智能指针与现代内存管理
C++ 智能指针与现代内存管理

深入讲解 C++ 现代内存管理的核心工具——智能指针,涵盖 unique_ptr 独占所有权语义、shared_ptr 引用计数机制与循环引用问题、weak_ptr 弱引用的应用场景、make_unique/make_shared 工厂函数的性能优势、自定义删除器的编写、RAII 资源管理思想的实践,以及从裸指针迁移到智能指针的重构策略,帮助开发者编写安全无泄漏的现代 C++ 代码。

399

2026.04.23

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

3248

2023.10.19

Go-Zero API开发使用教程
Go-Zero API开发使用教程

本专题聚焦Go-Zero的API开发,拆解.api描述文件的语法结构、路由与分组定义、参数绑定与校验规则,说明统一响应体、JWT鉴权、跨域与接口文档生成的落地方式,并给出网关聚合与接口测试的实践建议。

40

2026.11.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C++ Core Guidelines
C++ Core Guidelines

共0课时 | 0人学习

C++ Reference
C++ Reference

共0课时 | 0人学习

C++ 官方标准说明
C++ 官方标准说明

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn