c++如何实现一个高性能的环形队列(Ring Buffer)_c++无锁实现方法【并发】
技术百科
冰火之心
发布时间:2026-01-01
浏览: 次 高性能环形队列需无锁设计+原子操作+内存序控制:SPSC模式仅用relaxed内存序的原子索引,MPMC需CAS与acquire/release配对;须防伪共享、禁存非平凡类型,并优先选用成熟库。
用 C++ 实现高性能环形队列,核心在于**无锁(lock-free)设计 + 原子操作 + 内存序控制**。它适用于高并发生产/消费场景(如日志缓冲、网络收发包、实时数据流),避免互斥锁带来的线程阻塞和上下文切换开销。
一、基础结构:单生产者单消费者(SPSC)最简单且高效
这是唯一能真正“免原子操作”的无锁模式——生产者只改 write_index,消费者只改 read_index,两者互不干扰。只需用 std::atomic 保证读写可见性,内存序用 memory_order_relaxed 即可(因为无竞争)。
- 用
std::array或new T[N]分配连续内存,N 必须是 2 的幂(方便位运算取模) - 索引用
size_t,通过index & (N-1)替代% N,避免除法开销 - 两个原子索引:
std::atomicm_read{0}, m_write{0} - 判空:
read == write;判满:(write + 1) & mask == read(预留一个空位避免歧义)
二、多生产者多消费者(MPMC):必须用原子操作 + 内存序协调
多个线程可能同时修改同一索引,必须用 CAS(compare-and-swap)+ 合理内存序。常用策略是「先占位再填值」:
- 生产者:用
fetch_add争抢写位置 → 得到 slot → 写入元素 → 最后用store更新 write_index(memory_order_release) - 消费者:类似,先
fetch_add读位置 → 读取 → 最后更新 read_index(memory_order_release) - 关键点:读写操作本身需
用 memory_order_acquire/memory_order_release配对,确保元素写入对消费者可见 - 注意 ABA 问题?在 SPSC 中不存在;MPMC 中若用指针或复杂状态,需结合版本号(如
std::atomic高32位存版本)
三、内存布局与缓存友好优化
性能瓶颈常在 CPU 缓存行(cache line)伪共享(false sharing)。避免多个原子变量落在同一 cache line(通常 64 字节):
- 把
m_read和m_write放在不同 cache line:用alignas(64)对齐,或填充 padding - 缓冲区本身要
alignas(64),尤其当 T 是 POD 类型时,提升访存效率 - 避免在循环中频繁访问非局部变量;热数据(如索引)尽量贴近使用位置
四、实用建议与避坑点
- 不要自己造轮子:优先考虑成熟库,如
boost::lockfree::queue(MPMC)、moodycamel::ConcurrentQueue(高性能 MPMC,支持异常安全) - SPSC 场景下,Linux 的
eventfd或pipe也可作轻量级 ring buffer 替代,但受限于系统调用开销 - 禁止在 ring buffer 元素中存放需析构的对象(如
std::string)——无锁结构无法安全调用 dtor;应存储 trivially copyable 类型,或用 placement new + 显式析构(需额外同步) - 调试时可用
std::atomic_thread_fence插桩验证顺序,但发布版务必删掉
基本上就这些。无锁 ring buffer 不复杂但容易忽略内存序和缓存细节。从 SPSC 入手验证逻辑,再逐步扩展到 MPMC,比一上来啃复杂模型更稳妥。
# 放在
# 这是
# 多个
# 适用于
# 最简单
# linux
# 循环
# 并发
# 对象
# c++
# String
# 字节
# 指针
# 线程
# 无锁
# 高性能
# 局部变量
# 落在
# 性能瓶颈
# 中不
# Array
# nas
# padding
# 最后更新
# 可作
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- 如何将竖排文本文件转换为横排字符串
- Win11怎么关闭触控板_Win11笔记本禁用触摸
- Windows10系统怎么查看显卡型号_Win10
- 如何在Golang中使用container/hea
- php订单日志怎么记录评价_php记录订单评价日志
- PHP cURL GET请求:正确设置认证与自定义
- 如何使用Golang sync.Map实现并发安全
- php订单日志权限怎么设_php订单日志文件权限设
- c# 服务器GC和工作站GC的区别和设置
- Drupal 中 HTML 链接被重复转义导致渲染
- Win11怎么设置屏保时间_调整Win11屏幕保护
- Win11系统更新后黑屏怎么办 Win11更新黑屏
- XSLT怎么生成动态的HTML属性名和标签名
- Win11截图快捷键是什么_Win11自带截图工具
- GML (Geography Markup Lan
- Windows系统时间服务错误_W32Time服务
- Win11怎么更改任务栏颜色_Windows11个
- C++中的constexpr和const有什么区别
- Mac版Final Cut Pro入门_Mac视频
- Python与OpenAI接口集成实战_生成式AI
- Dapper的Execute方法的返回值是什么意思
- Win11怎么开启HDR模式_Windows 11
- 如何在Golang中使用闭包_封装变量与函数作用域
- Win11资源管理器卡顿怎么办 Win11文件资源
- Win11怎么设置按流量计费_Win11限制后台流
- Win11怎么关闭触摸屏_禁用Win11笔记本触摸
- Win11怎么设置DNS服务器_Windows11
- Windows 11怎么设置默认解压软件_Wind
- Win11怎么设置开机密码_Windows11账户
- 如何使用Golang理解结构体指针方法接收者_Go
- php嵌入式需要什么环境_搭建php+linux嵌
- PHP主流架构如何处理会话管理_Session与C
- Win11相机打不开提示错误怎么修_相机权限开启与
- mac怎么安装pip_MAC Python pip
- Win10怎么限制单程序CPU占用上限_Win10
- 如何使用Golang模拟请求超时_Golang c
- Win11怎么硬盘分区 Win11新建磁盘分区详细
- php订单日志怎么按状态筛选_php筛选不同状态订
- windows如何测试网速_windows系统网络
- 如何使用Golang sort排序切片_Golan
- Win11更新后变慢怎么办_Win11系统更新后卡
- 如何在Golang中写入JSON文件_保存结构体数
- 如何在Golang中操作嵌套切片指针_Golang
- Win10电脑怎么设置休眠快捷键_Windows1
- 如何使用正则表达式批量替换重复的 *- 模式为固定
- C#如何在一个XML文件中查找并替换文本内容
- 如何在 Go 中创建包含映射(map)的切片(sl
- 如何使用Golang匿名函数_快速定义临时函数逻辑
- Win11怎么关闭自动更新 Win11永久关闭系统
- Ajax提交表单PHP怎么接收_处理Ajax发送的

用
QQ客服