☰
ABA 问题,一句话
2026/10/9 2:19:26 网站建设 项目流程

地址没变,但里面的东西已经换了一茬,CAS 被骗了。


打个比方

你桌上放了杯奶茶,你瞅了一眼:是珍珠奶茶

你转身去接个电话。

这 5 分钟里:

  1. 同事把奶茶喝完了,杯子扔了

  2. 保洁阿姨把杯子捡回来,又倒了杯新的珍珠奶茶进去

  3. 杯子还是那个杯子,位置也没变

你回来一看:哟,奶茶还在!

于是你放心地拿起来喝了一口。

但这是另一杯奶茶了。


对应到代码里

CAS 就是那个"瞅一眼"的操作:

// 你瞅了一眼:head 指向地址 A old = head.load(); // 你去干别的事了(被别的线程抢占了) // 这时候: // 别的线程把 A 弹走了 // 释放了 A 的内存 // 又分配了新节点,刚好分配到同一个地址 A // 你回来,CAS 检查:head 还是 A 吗? // 是!地址一样!CAS 成功! head.compare_exchange_strong(old, new_ptr);

CAS 只认地址,不认"这个地址里装的是不是原来的东西"。


为什么叫 ABA

因为变化路径是:

A → B → A

你开头看到 A,结尾看到 A,中间 B 发生过啥,CAS 根本不知道。

就像你早上看女朋友在沙发上,晚上看还在沙发上,中间她换了个灵魂你不知道。


为什么这么坑

因为 CAS 成功后,你拿着"旧记忆"去操作:

  • 你以为A->next还是原来的 B

  • 其实 A 里面的数据已经全换了

  • 你基于错误的前提改结构,链表断、内存重复释放、读到别人的数据

而且这种 bug 特别阴间:

  • 单线程跑:永远不出

  • 低并发跑:几个月不出

  • 高并发压测:随机崩,还不一定能复现


怎么治

1. 加版本号——给每个地址办个"身份证"

A:第1代 → B:第2代 → A:第3代

你手上是 A 第 1 代,现在系统是 A 第 3 代,CAS 一眼识破。

2. 不让内存复用——别人还在用 A 的时候,谁都不许 delete A

等确认没人用了,再真正释放。地址永远不会"死而复生"。

3. 用句柄不用裸指针——发身份证,不发人脸

(槽位3,第5代)

槽位 3 后来被复用,但代次变成 6,你拿第 5 代的票来用,系统直接拒。


一句话总结

CAS 是"认脸"的,ABA 就是:脸没变,魂换了。

版本号让它能认魂,Hazard Pointer 不让魂投胎,句柄直接不发人脸,发身份证。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询