You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用compare_exchange_weak遇悬空指针会怎样?无锁栈Push为何安全?

关于无锁栈push中new_node->next的悬空指针疑问及compare_exchange_weak传入悬空指针的后果

一、push中的new_node->next会不会成为悬空指针?

在Anthony Williams的无锁栈初始实现里,不会。核心原因是pop操作的内存管理设计:

  • 线程执行pop时,仅将目标节点从栈的原子head中移除,不会立即释放节点内存,而是暂存到一个待删除列表(比如std::list<Node*>)中。
  • 只有确认没有任何线程还在访问该节点时,才会真正释放其内存(通常通过引用计数、或利用内存模型特性判断节点不再被访问)。

再看push的核心流程:

void push(T data) {
    Node* new_node = new Node(data);
    new_node->next = head.load();
    while (!head.compare_exchange_weak(new_node->next, new_node)) {}
}

new_node->next初始指向的是当前head的快照值。即便在load()之后、CAS操作之前,该快照对应的节点被其他线程pop,节点也只是被移到待删除列表,并未被释放——因此new_node->next指向的内存依然有效,不会成为悬空指针。直到CAS操作完成(新节点成功插入栈),原节点的释放也不会影响新节点next指针的有效性:此时栈的结构已由新head维护,其他线程访问栈时只会通过head遍历,不再依赖已被删除节点的next。

二、调用compare_exchange_weak时传入悬空指针会有什么后果?

compare_exchange_weak针对原子指针操作的是指针的地址值,而非指针指向的内存内容,但传入悬空指针依然会引发严重问题,核心是悬空指针本身属于C++标准中的未定义行为范畴,具体场景的后果如下:

  1. 作为预期值传入:
    若预期值是悬空指针(对应内存已释放),CAS会将原子变量的当前地址值与该悬空地址做比较。如果恰好有新内存分配到这个悬空地址,且原子变量当前值等于该地址,CAS会错误执行替换,直接破坏栈结构,导致数据丢失或线程安全问题。即便没有地址复用,使用悬空指针本身违反内存模型规则,编译器可能生成不可预测的代码(比如优化掉相关操作),导致逻辑彻底混乱。

  2. 作为新值传入:
    若将悬空指针作为新值传入CAS,会让原子指针指向已释放的内存。后续任何访问该原子指针的操作(比如push时的load()、pop时的节点遍历)都会触发对悬空内存的访问,直接导致程序崩溃、数据损坏或其他不可预测行为。

无论哪种情况,传入悬空指针给compare_exchange_weak都会引入未定义行为,是必须严格避免的错误。

内容的提问来源于stack exchange,提问作者teddy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 13:49:59