不能直接用 std::atomic 做节点指针的原子操作——它只保证指针本身的读写原子性,不保证“读旧值→算新值→写回”整套操作的原子性;插入需用 compare_exchange_weak 实现 cas 语义以避免丢节点,next 可为普通指针但须确保构造完成后再发布。

std::atomic 能直接用于链表节点指针吗?
不能直接用 std::atomic<t></t> 做节点指针的原子操作——它只保证指针本身的读写原子性,不保证“读旧值→算新值→写回”这一整套操作的原子性。插入单向链表必须完成「读取 head → 构造新节点并指向原 head → 原子地把 head 指向新节点」,这需要 compare_exchange_weak 或 compare_exchange_strong,而它们要求目标变量是 std::atomic<node></node> 类型,且操作逻辑必须手动编排。
插入时为什么必须用 compare_exchange_weak 而不是 store?
因为多个线程可能同时尝试插入:如果只用 store,后执行的线程会直接覆盖前一个插入,导致丢节点。只有 compare_exchange_weak 能实现「CAS(Compare-And-Swap)」语义:仅当当前 head 值等于我读到的旧值时,才把 head 更新为新节点;否则重试。这是无锁插入的核心保障。
-
compare_exchange_weak可能因硬件原因虚假失败(spurious failure),所以必须放在循环里重试 - 返回
true表示成功更新,false表示 head 已被其他线程改过,需重新读取再试 - 不要用
compare_exchange_strong—— 它在某些平台(如 ARM)开销更大,且对插入场景无实质优势
Node 结构体里 next 成员要不要也用 std::atomic?
要,但仅限于插入/删除逻辑中被并发读写的字段。插入时只修改 head 指针,新节点的 next 字段在构造时就设为旧 head 值,之后不再修改;只要确保构造完成后再发布(publish),其他线程看到的 next 就是稳定值。所以 next 可以是普通指针,但必须满足:
- 新节点内存必须在调用
compare_exchange_weak前分配好、初始化完毕(包括next) - 不能用栈上临时对象或未同步发布的堆内存——否则其他线程可能读到未初始化的
next - 若后续要支持删除或遍历中的安全释放,
next才需升级为std::atomic<node></node>并配合 hazard pointer 或 RCU
最简可行插入代码长什么样?
以下是最小可运行片段,不含内存回收,仅演示插入逻辑:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
struct Node {
int data;
Node* next;
Node(int d) : data(d), next(nullptr) {}
};
<p>std::atomic<node> head{nullptr};</node></p><p>void push(int data) {
Node<em> node = new Node(data);
Node</em> expected;
do {
expected = head.load();
node->next = expected;
} while (!head.compare_exchange_weak(expected, node));
}</p>
注意 expected 是按值传递给 compare_exchange_weak 的,函数内部会把它当作“期望值”去比对;一旦失败,expected 会被自动更新为当前实际值,所以下次循环无需重新 load。
真正难的不是写对这十几行,而是确保所有线程看到的节点内存生命周期受控——无锁结构里,释放内存永远比分配更危险。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










