讲师中心 微信公众号
AI工具推荐 视频效率加速

C++如何实现一个简单的线程安全无锁单向链表(原子指针版)

冬强酱_3935

冬强酱_3935

发布时间:2026-07-10 07:28:58

|

968人浏览过

|

来源于php中文网

原创

不能直接用 std::atomic 操作链表节点指针,因其仅保证指针读写原子性,不保证“读→解引用→改next→写回”整套操作原子性,易引发ABA问题和use-after-free;需结合版本号与延迟回收机制。

c++如何实现一个简单的线程安全无锁单向链表(原子指针版)

为什么不能直接用 std::atomic<t></t> 操作链表节点指针

因为 std::atomic<t></t> 只保证指针本身的读写原子性,不保证「读指针 → 解引用 → 修改 next 字段 → 写回」这一整套操作的原子性。典型问题出现在 pop():你读到 head,准备把它摘下来,但另一线程可能刚把新节点插入到它前面,此时你 CAS 更新 head 会失败——这本身没问题;但更危险的是,若你已解引用了旧 head 并准备 delete 它,而它其实在被其他线程当作中间节点使用(比如正在被 push() 链入),就会造成 use-after-free。

根本矛盾在于:无锁结构必须避免 ABA 问题,且内存回收不能依赖引用计数或垃圾收集器。C++11 起,std::atomic<t></t> 不提供内置的 ABA 防御机制,必须手动扩展指针位宽(如用 std::atomic<uint64_t></uint64_t> 打包指针+版本号)或借助 std::atomic<:shared_ptr></:shared_ptr>(但后者有性能开销且非真正无锁)。

std::atomic<uint64_t></uint64_t> 实现带版本号的原子指针

将指针低 48 位存地址、高 16 位存版本号(假设指针是 48 位有效地址,常见于 x86_64 Linux 的用户空间),每次 CAS 前递增版本号,即可规避 ABA。注意:不能直接对裸指针做位运算,需先转为 uintptr_t

  • 定义节点结构时,next 字段类型必须是 std::atomic<uint64_t></uint64_t>,而非 Node*
  • 读取 next 指针:用 next.load(std::memory_order_acquire),再用位掩码提取地址(如 ptr & 0x0000FFFFFFFFFFFFULL
  • CAS 更新 next:构造新值 (new_ptr & mask) | ((old_version + 1) ,再调用 <code>next.compare_exchange_weak()
  • 务必使用 std::memory_order_acq_rel 或更强序,尤其在 push() 的 CAS 处,否则其他线程可能看到部分初始化的节点

示例片段:

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

C++
C++

"空空如也"

下载
struct Node {
    int data;
    std::atomic<uint64_t> next{0};
<pre class="brush:php;toolbar:false;">Node(int d) : data(d) {}

};

struct LockFreeStack { std::atomic head{0};

void push(Node* node) {
    uint64_t old = head.load(std::memory_order_acquire);
    do {
        node->next.store(old, std::memory_order_relaxed);
        // 提取当前 head 地址和版本
        uintptr_t ptr = old & 0x0000FFFFFFFFFFFFULL;
        uint16_t ver = (old >> 48) & 0xFFFF;
        uint64_t desired = (reinterpret_cast<uintptr_t>(node) & 0x0000FFFFFFFFFFFFULL)
                         | (static_cast<uint64_t>(ver + 1) << 48);
        if (head.compare_exchange_weak(old, desired, std::memory_order_acq_rel))
            break;
    } while (true);
}

};

pop() 必须分离「摘链」和「释放内存」两个阶段

这是最容易出错的地方。无锁结构中,一个节点可能被多个线程同时观察到(比如正被遍历),因此不能在 pop() 中直接 delete。必须引入延迟回收机制,例如:Hazard PointerRCU。对于简单场景,可采用「惰性删除 + 周期性扫描」:每个线程维护本地待回收列表,pop() 仅把节点加入该列表;另起后台线程定期检查是否所有线程都已不再访问这些节点,再统一 delete

  • 切勿在 pop() 的 CAS 成功后立刻 delete node —— 此时别的线程可能刚读到该节点地址,正准备访问其 next
  • 如果不用 Hazard Pointer,至少应让 pop() 返回 std::unique_ptr<node></node>,把内存管理责任交给调用方,明确语义边界
  • 即使使用 std::shared_ptr 管理节点,也要注意:shared_ptr 的控制块本身不是无锁的,use_count 的修改仍涉及锁,不符合“无锁”定义

单向链表的无锁 find() 和遍历为何天然安全

因为只读操作不修改结构,只要节点一旦被 push() 成功链入,其 next 字段就不会再被原线程改写(后续修改只发生在新插入节点上)。所以从 head 开始逐个读 next 是安全的,无需额外同步。

  • 遍历时用 std::memory_order_acquire 读每个节点的 next,确保看到该节点完整初始化的数据(如 data 字段)
  • 但要注意:遍历结果不具快照一致性 —— 中间可能有节点被其他线程 pop() 掉,你仍会读到它的 data,只是下一步 next 会是 null 或跳到别处
  • 如果业务需要强一致性遍历(比如统计长度),就必须加锁,或者改用带标记删除的双链表 + 垃圾收集方案

真正的难点不在链表逻辑本身,而在内存生命周期管理。很多人卡在 CAS 成功后删节点,却忘了其他线程栈上可能还存着那个地址的拷贝。

热门AI工具

更多
豆包大模型

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

蛙蛙写作

一款AI论文写作工具,主要用于超级AI智能写作助手,适合需要提升相关任务效率的用户。

立刻MV
立刻MV Hot

立刻MV是一款AI文本写作工具,AI 音乐视频(MV)创作工具。

Seko
Seko Hot

一款AI视频创作工具,主要用于商汤科技推出的创编一体的AI短视频创作Agent,适合需要提升相关任务效率的用户。

DeepSeek

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

WorkBuddy

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

Atoms
Atoms Hot

Atoms是一款AI智能体工具,第一支自动构建真实业务的 AI 团队。

超级简历WonderCV

一款AI办公效率工具,主要用于免费求职简历模版下载制作,应届生职场人必备简历制作神器,适合需要提升相关任务效率的用户。

二狗PPT
二狗PPT Hot

一款AI演示文稿工具,主要用于专为中式职场打造的AI PPT生成工具,适合需要提升相关任务效率的用户。

相关专题

更多
c++和c语言的区别有哪些
c++和c语言的区别有哪些

c++和c语言的区别:1、面向对象编程(OOP)支持不同;2、新增特性不同;3、标准库不同;4、编译方式不同;5、命名空间不同等等。想了解更多c++的相关内容,可以阅读本专题下面的文章。

2068

2024.03.14

c++和python学习顺序推荐
c++和python学习顺序推荐

一般建议先学习C++,再学习Python,因为这样可以逐步从较为底层的编程语言向更高级的语言过渡。想了解更多python的相关内容,可以阅读本专题下面的文章。

959

2024.03.14

python和c++学习性价比分析
python和c++学习性价比分析

Python易于学习,广泛应用于Web开发、数据科学和人工智能等领域,但性能较低。C语言性能高,适用于对性能要求较高的场景,如游戏开发和系统编程,但学习曲线陡峭,错误处理复杂。想了解更多python的相关内容,可以阅读本专题下面的文章。

367

2024.03.14

c语言和c++一样吗
c语言和c++一样吗

c语言和c++是两种不同的编程语言,虽然有相似之处,但存在显著差异。c语言专注于过程式编程和系统级开发,以简洁、高效著称。c++作为c语言的超集,引入了面向对象编程,增强了代码组织和管理能力,但学习曲线也更陡峭。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

307

2024.03.14

c语言和c++先学哪个好
c语言和c++先学哪个好

初学者选择学习c语言还是c++语言,需要根据个人学习目标、背景以及编程兴趣和预期应用方向来决定。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

346

2024.03.14

c语言和c++的区别和联系
c语言和c++的区别和联系

c语言和c++是计算机科学领域应用广泛的编程语言。虽然它们有着相似的基础,但它们在语言类型、语法功能和内存管理方面存在着显著差异。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

560

2024.03.14

c++软件中文更改教程
c++软件中文更改教程

对于 ide,可通过打开设置,找到语言设置,选择中文,并保存更改。对于非 ide 应用程序,可查找设置或选项,选择语言设置,更改为中文,并保存更改。想了解更多c++的相关内容,可以阅读本专题下面的文章。

1389

2024.03.21

python和java和c++学习性价比分析
python和java和c++学习性价比分析

Python以其易学性、丰富的库和活跃的社区而著称,适合数据科学、人工智能和Web开发。Java以其跨平台性、企业级应用开发和Android应用开发而闻名。C++以其底层控制能力、高效性能和游戏开发而著称。选择哪种语言取决于个人兴趣、职业方向和特定需求。想了解更多python和java和c++的相关内容,可以阅读本专题下面的文章。

1177

2024.03.22

Buffalo框架数据库开发全教程
Buffalo框架数据库开发全教程

本专题围绕Buffalo框架数据库开发,讲解database.yml多环境配置、soda与fizz迁移生成回滚、模型结构体标签、增删改查与条件查询、一对多与多对多关联、数据校验、回调钩子、事务处理及原生SQL执行能力。

0

2026.09.23

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Conan 2 Essentials 免费课程
Conan 2 Essentials 免费课程

共0课时 | 0人学习

CMake 与 Conan 集成实践
CMake 与 Conan 集成实践

共0课时 | 0人学习

Conan 2 高级依赖模型介绍
Conan 2 高级依赖模型介绍

共0课时 | 0人学习

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

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