在 Golang 中,原子操作(atomic)用于对整数进行原子读写操作。原子操作可以确保对整数的并发访问是安全的。
atomic 包提供了以下原子操作:
- AddInt32/AddUint32/AddInt64/AddUint64:原子的整数加操作
- CompareAndSwapInt32/CompareAndSwapUint32/CompareAndSwapInt64/CompareAndSwapUint64:CAS操作,比较并交换
- LoadInt32/LoadUint32/LoadInt64/LoadUint64:原子的读取操作
- StoreInt32/StoreUint32/StoreInt64/StoreUint64:原子的写入操作
- SwapInt32/SwapUint32/SwapInt64/SwapUint64:原子的交换操作
例如:
var counter int32
func inc() {
atomic.AddInt32(&counter, 1)
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < 1000; j++ {
inc()
}
}()
}
wg.Wait()
println(atomic.LoadInt32(&counter)) // 10000
}
这里使用 atomic.AddInt32() 对 counter 进行原子增加,最终结果是正确的 10000。
如果使用普通的读写操作:
var counter int32
func inc() {
counter++
}
// ...
println(counter) // 结果是未知的
很可能出现竞争条件,最终打印的结果是未知的。
再看一个 CAS 的例子:
var lock bool
func tryLock() bool {
return atomic.CompareAndSwapInt32(&lock, 0, 1)
}
func unlock() {
atomic.StoreInt32(&lock, 0)
}
这里实现了一个简单的互斥锁,使用 CAS 操作复原和设置 lock。
所以原子操作主要有以下作用:
- 线程安全的改变某个整数值。
- 实现简单的锁、互斥量等同步原语。
- 避免竞争条件的出现。
虽然 Golang 的 Channel 和 Mutex 可以实现更高级的同步操作,但是原子操作由于其简单高效的特点,在一些场景下也有很好的应用。
所以总结来说,原子操作是 Golang 在语言层面提供的一种轻量级的同步机制。它通过特定的汇编指令实现,可以保证读写操作的原子性。对整数类型有比较广泛的支持,可以实现一些简单的同步原语。