推荐答案
// www.javascriptcn.com code example
package main
import (
"fmt"
"sync/atomic"
"time"
)
func main() {
var counter int64
// 启动多个goroutine并发地增加计数器
for i := 0; i < 10; i++ {
go func() {
for j := 0; j < 1000; j++ {
atomic.AddInt64(&counter, 1)
}
}()
}
// 等待所有goroutine完成
time.Sleep(time.Second)
// 读取最终的计数器值
fmt.Println("Final Counter:", atomic.LoadInt64(&counter))
}本题详细解读
sync/atomic 包的作用
sync/atomic 包提供了底层的原子操作,用于在多线程(goroutine)环境下安全地操作共享变量。这些操作是硬件级别的原子操作,能够确保在并发环境下不会出现数据竞争问题。
常用函数
AddInt64: 原子地增加或减少一个
int64类型的值。atomic.AddInt64(&counter, 1) // 增加1 atomic.AddInt64(&counter, -1) // 减少1
LoadInt64: 原子地读取一个
int64类型的值。value := atomic.LoadInt64(&counter)
StoreInt64: 原子地存储一个
int64类型的值。atomic.StoreInt64(&counter, 100)
SwapInt64: 原子地交换一个
int64类型的值,并返回旧值。oldValue := atomic.SwapInt64(&counter, 200)
CompareAndSwapInt64: 原子地比较并交换一个
int64类型的值,只有在当前值等于预期值时才会进行交换。swapped := atomic.CompareAndSwapInt64(&counter, 100, 200)
使用场景
- 计数器: 在多个 goroutine 中并发地增加或减少一个计数器。
- 标志位: 使用原子操作来设置或清除一个标志位,确保在并发环境下不会出现竞争条件。
- 无锁数据结构: 在实现无锁队列、栈等数据结构时,原子操作是必不可少的。
注意事项
- 原子操作通常用于简单的数据类型(如
int32,int64,uint32,uint64等)。 - 对于复杂的数据结构(如结构体、切片等),原子操作无法保证其原子性,需要使用其他同步机制(如
sync.Mutex)。 - 原子操作的性能通常比锁要高,但在高并发场景下,仍然需要谨慎使用,避免成为性能瓶颈。