Golang中使用原子操作时缓存一致性会导致某些线程无限循环吗?
Golang中使用原子操作时缓存一致性会导致某些线程无限循环吗?
当我使用以下测试代码时,某些线程会陷入循环。但如果我们添加了 println()、os.Create() 或 os.Open(),这种情况就不会发生。
var casValue = new(int64)
*casValue = 0
var old int64 = 0
var new int64 = 1
var container *list.List
container = list.New()
var wg sync.WaitGroup
wg.Add(10)
t1 := time.Now()
for i := 0; i < 10; i++ {
go func(i int) {
for j := 0; j < 10000; {
if atomic.CompareAndSwapInt64(casValue, old, new) {
var test = "hello world,hello world,hello world"
container.PushBack(test)
*casValue = 0
j++
}
//println("hello")、os.Create("test")、os.Open("test") will solve this problem
}
println("casValue=", *casValue)
wg.Done()
}(i)
}
lentcy := time.Since(t1)
wg.Wait()
fmt.Println(container.Len())
更多关于Golang中使用原子操作时缓存一致性会导致某些线程无限循环吗?的实战教程也可以访问 https://www.itying.com/category-94-b0.html
3 回复
当我取消 println(“hello”) 的注释时,没有数据冲突。原因是什么?
更多关于Golang中使用原子操作时缓存一致性会导致某些线程无限循环吗?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Go版本从1.13.5升级到1.14.1后,问题已解决,非常感谢!
这是一个典型的缓存一致性问题导致的线程饥饿现象。在Go语言中,原子操作本身是线程安全的,但内存可见性需要依赖正确的同步机制。
问题出现在以下代码段:
if atomic.CompareAndSwapInt64(casValue, old, new) {
var test = "hello world,hello world,hello world"
container.PushBack(test)
*casValue = 0 // 这里直接赋值,不是原子操作
j++
}
主要问题:
- 非原子写操作:
*casValue = 0不是原子操作,多个goroutine可能同时看到不一致的值 - 缺少内存屏障:直接赋值不会创建内存屏障,其他CPU核心可能看不到最新的值
- container非线程安全:
list.List不是并发安全的,多个goroutine同时调用PushBack()会导致数据竞争
修复方案:
package main
import (
"container/list"
"fmt"
"sync"
"sync/atomic"
"time"
)
func main() {
var casValue int64 = 0
var container = list.New()
var mu sync.Mutex
var wg sync.WaitGroup
wg.Add(10)
t1 := time.Now()
for i := 0; i < 10; i++ {
go func(i int) {
for j := 0; j < 10000; {
// 使用原子操作检查和设置
if atomic.CompareAndSwapInt64(&casValue, 0, 1) {
mu.Lock()
var test = "hello world,hello world,hello world"
container.PushBack(test)
mu.Unlock()
// 使用原子操作重置
atomic.StoreInt64(&casValue, 0)
j++
}
}
wg.Done()
}(i)
}
wg.Wait()
fmt.Println("container length:", container.Len())
fmt.Println("time elapsed:", time.Since(t1))
}
或者使用更简洁的sync/atomic方案:
func main() {
var casValue int64 = 0
var counter int64 = 0
var wg sync.WaitGroup
wg.Add(10)
for i := 0; i < 10; i++ {
go func() {
for j := 0; j < 10000; {
if atomic.CompareAndSwapInt64(&casValue, 0, 1) {
// 执行需要同步的操作
atomic.AddInt64(&counter, 1)
// 使用原子存储确保可见性
atomic.StoreInt64(&casValue, 0)
j++
}
}
wg.Done()
}()
}
wg.Wait()
fmt.Println("total operations:", atomic.LoadInt64(&counter))
}
关于你提到的println()、os.Create()等调用能解决问题,这是因为:
- 这些系统调用会隐式创建内存屏障
- 它们会触发goroutine调度,让其他goroutine有机会执行
- 但这不是正确的解决方案,只是掩盖了问题
正确的做法是:
- 对共享变量的所有访问都使用原子操作
- 对非线程安全的数据结构使用互斥锁保护
- 使用
atomic.Store和atomic.Load确保内存可见性

