Files
bulma/cas_aba_demo.go

109 lines
2.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
import (
"fmt"
"sync"
"sync/atomic"
"time"
)
// =============== ABA 问题演示(增强时序稳定性) ===============
func demoABAProblem() {
var value int32 = 100
var wg sync.WaitGroup
ready := make(chan struct{}) // 同步信号:确保 A 先读取
wg.Add(2)
// Goroutine A: 读取初始值后等待信号
go func() {
defer wg.Done()
old := atomic.LoadInt32(&value) // 读取到 100
<-ready // 等待 B 完成 ABA 修改
if atomic.CompareAndSwapInt32(&value, old, 200) {
fmt.Println("⚠️ ABA 问题触发: CAS 成功!但值曾被篡改 (100→150→100)")
} else {
fmt.Println("❌ 未复现 ABA(调度异常)")
}
}()
// Goroutine B: 执行 A→B→A 修改
go func() {
defer wg.Done()
time.Sleep(5 * time.Millisecond) // 确保 A 已读取
atomic.StoreInt32(&value, 150) // A→B
atomic.StoreInt32(&value, 100) // B→A
close(ready) // 通知 A 执行 CAS
}()
wg.Wait()
fmt.Printf("最终值: %d (ABA 发生时应为 200)\n", value)
}
// =============== 安全方案:位域编码防 ABA ===============
// 64位结构: [高32位: 版本号] | [低32位: 实际值]
type VersionedInt32 struct {
data int64 // 原子存储单元
}
// 合并值与版本号
func pack(val, ver int32) int64 {
return (int64(ver) << 32) | int64(uint32(val))
}
// 拆解:返回 (值, 版本号)
func unpack(data int64) (val int32, ver int32) {
return int32(data), int32(data >> 32) // 低32位=值, 高32位=版本
}
// 安全读取当前值
func (v *VersionedInt32) Load() int32 {
val, _ := unpack(atomic.LoadInt64(&v.data)) // 取第一个返回值
return val
}
// 带版本号的原子 CAS(彻底规避 ABA)
func (v *VersionedInt32) CAS(oldVal, newVal int32) bool {
for {
current := atomic.LoadInt64(&v.data)
curVal, curVer := unpack(current)
if curVal != oldVal {
return false // 值已变更,拒绝更新
}
// 构造新状态:值更新 + 版本号+1
newState := pack(newVal, curVer+1)
if atomic.CompareAndSwapInt64(&v.data, current, newState) {
return true
}
// 失败则重试
}
}
func main() {
fmt.Println("=== ABA 问题演示(通道同步确保时序) ===")
demoABAProblem()
fmt.Println("\n=== 安全方案验证(位域编码) ===")
vv := &VersionedInt32{data: pack(100, 0)} // 初始值=100, 版本=0
// 首次 CAS100 → 200
if vv.CAS(100, 200) {
val := vv.Load()
_, ver := unpack(atomic.LoadInt64(&vv.data))
fmt.Printf("✅ CAS 成功 | 值: %d, 版本: %d\n", val, ver)
}
// 尝试用过期旧值 100 更新(应失败)
if !vv.CAS(100, 300) {
fmt.Println("✅ 检测到非法修改:CAS 拒绝使用过期旧值更新")
}
// 使用当前正确值更新
if vv.CAS(200, 300) {
fmt.Printf("✅ 安全更新成功 | 当前值: %d\n", vv.Load())
}
}