Golang中atomic.LoadInt64打印值顺序不正确的问题
Golang中atomic.LoadInt64打印值顺序不正确的问题
我以某种方式实现了 atomic.LoadInt64,期望它能按顺序打印值。但它打印的是随机值。这里有什么问题?
由于我对原子操作理解不深,我在这里遗漏了什么概念,导致无法正确实现它?
package main
import (
"fmt"
"sync"
"sync/atomic"
)
var wg sync.WaitGroup
var counter int64
func concurrencyGenerator() {
gs := 1000
wg.Add(gs)
for i := 0; i < gs; i++ {
go func() {
atomic.AddInt64(&counter, 1)
fmt.Println("Value : ", atomic.LoadInt64(&counter))
wg.Done()
}()
}
}
func main() {
concurrencyGenerator()
wg.Wait()
fmt.Println(counter)
}

更多关于Golang中atomic.LoadInt64打印值顺序不正确的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
我无意冒犯,但在原子操作方面,我不得不问:如果你对原子操作没有深入的理解,为什么要使用它们呢?如果是为了学习原子操作,那就忽略我的问题。但我想确认,你是否遇到了某个问题,并且在尚未理解原子操作应该如何工作的情况下,就认为原子操作是解决方案。不要陷入那个老生常谈的工具定律认知偏差!
原子操作本身并不同步。它们只是确保操作完全执行,或者在可能失败的操作中,失败意味着操作从未发生,因此你可以重试而不会导致数据不一致。
如果你希望按顺序访问计数器,那么不要使用并发,直接使用循环即可。并行/并发的全部意义在于让不同的执行过程同时运行(因此任务#1与#2同时运行,所以它们可能以任何顺序完成)。
尽管这是针对C++的,但我强烈推荐Fedor Pikus在CppCon上关于原子操作/无锁/性能的视频:
问题在于你对原子操作的顺序保证存在误解。atomic.LoadInt64 确实能保证读取操作的原子性,但它不保证 goroutine 的执行顺序。
在你的代码中,1000个 goroutine 并发执行,虽然每个 AddInt64 和 LoadInt64 操作本身是原子的,但 goroutine 的调度顺序是随机的。这意味着:
- 某个 goroutine 可能先增加计数器
- 但另一个 goroutine 可能先打印值
- 打印的顺序完全取决于 goroutine 的调度时机
如果你需要按顺序打印,需要额外的同步机制。这里是一个使用通道来保证顺序的示例:
package main
import (
"fmt"
"sync"
"sync/atomic"
)
type orderedResult struct {
seq int
value int64
}
func main() {
const goroutines = 1000
var counter int64
var wg sync.WaitGroup
wg.Add(goroutines)
// 通道用于收集结果并按顺序输出
resultCh := make(chan orderedResult, goroutines)
orderCh := make(chan struct{}, 1) // 用于控制增加操作的顺序
// 启动一个 goroutine 专门负责按顺序打印
go func() {
results := make([]orderedResult, goroutines)
for i := 0; i < goroutines; i++ {
results[i] = <-resultCh
}
// 按顺序打印
for _, r := range results {
fmt.Printf("Value: %d\n", r.value)
}
}()
for i := 0; i < goroutines; i++ {
go func(seq int) {
defer wg.Done()
// 通过通道确保增加操作按顺序执行
orderCh <- struct{}{}
newVal := atomic.AddInt64(&counter, 1)
<-orderCh
// 将结果发送到通道,包含顺序编号
resultCh <- orderedResult{seq: seq, value: newVal}
}(i)
}
wg.Wait()
close(resultCh)
fmt.Printf("Final counter: %d\n", counter)
}
如果你只是需要最终看到所有值(不要求顺序),但希望看到每个 goroutine 读取时的准确值,可以这样修改原代码:
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
const goroutines = 1000
var counter int64
var wg sync.WaitGroup
wg.Add(goroutines)
// 使用互斥锁保护打印顺序(但执行顺序仍然是随机的)
var mu sync.Mutex
for i := 0; i < goroutines; i++ {
go func(id int) {
defer wg.Done()
newVal := atomic.AddInt64(&counter, 1)
mu.Lock()
fmt.Printf("Goroutine %d: Value: %d\n", id, newVal)
mu.Unlock()
}(i)
}
wg.Wait()
fmt.Printf("Final counter: %d\n", counter)
}
关键点:
atomic包保证单个操作的原子性,但不保证多个操作的执行顺序- goroutine 的调度是非确定性的
- 如果需要顺序性,需要额外的同步机制(如通道、互斥锁或条件变量)


