使用sync/atomic可实现并发安全的计数器,通过原子操作避免竞态条件,相比sync.Mutex性能更高,适用于单个变量的简单操作,如计数、标志位、指针更新等,但需注意对齐问题和不可用于复杂逻辑。

在Go语言中,当我们需要在多个goroutine之间安全地共享和更新一个计数器时,
sync/atomic
sync.Mutex
在并发编程中,对共享变量进行增减操作是一个常见的场景。如果直接使用
i++
i--
i++
sync/atomic
AddInt32
AddInt64
LoadInt32
StoreInt64
以下是一个使用
sync/atomic
立即学习“go语言免费学习笔记(深入)”;
package main
import (
"fmt"
"sync"
"sync/atomic"
"time"
)
func main() {
var counter int64 // 使用int64,因为atomic包提供了对int64的原子操作
var wg sync.WaitGroup
numWorkers := 1000
incrementsPerWorker := 1000
// 模拟并发递增
fmt.Println("开始并发递增...")
startTime := time.Now()
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < incrementsPerWorker; j++ {
atomic.AddInt64(&counter, 1) // 原子性地将counter加1
}
}()
}
wg.Wait() // 等待所有goroutine完成
endTime := time.Now()
fmt.Printf("原子操作递增完成,最终计数: %d, 耗时: %v\n", atomic.LoadInt64(&counter), endTime.Sub(startTime)) // 原子性地读取counter值
// 演示非原子操作的危险性(通常会得到错误结果)
var nonAtomicCounter int64
var wgNonAtomic sync.WaitGroup
fmt.Println("\n开始非原子递增(可能不准确)...")
startTimeNonAtomic := time.Now()
for i := 0; i < numWorkers; i++ {
wgNonAtomic.Add(1)
go func() {
defer wgNonAtomic.Done()
for j := 0; j < incrementsPerWorker; j++ {
nonAtomicCounter++ // 非原子操作
}
}()
}
wgNonAtomic.Wait()
endTimeNonAtomic := time.Now()
fmt.Printf("非原子操作递增完成,最终计数: %d (预期: %d), 耗时: %v\n", nonAtomicCounter, int64(numWorkers*incrementsPerWorker), endTimeNonAtomic.Sub(startTimeNonAtomic))
if nonAtomicCounter != int64(numWorkers*incrementsPerWorker) {
fmt.Println("警告:非原子操作导致计数不准确!")
}
}
在这个例子中,
atomic.AddInt64(&counter, 1)
counter
nonAtomicCounter++
当我们谈到Go语言中的并发,尤其是多个goroutine同时操作一个共享变量时,常规的加减操作(比如
counter++
counter--
counter++
counter
counter
问题就出在这里。如果两个或更多的goroutine几乎同时执行
counter++
counter
counter
0 + 1 = 1
counter
0 + 1 = 1
counter
counter
最终结果是
counter
sync/atomic
sync.Mutex
在Go语言中处理并发安全问题时,
sync/atomic
sync.Mutex
sync.Mutex
sync/atomic
AddInt64
StoreUint32
LoadUint32
StorePointer
LoadPointer
atomic
总结性区别:
atomic
Mutex
atomic
Mutex
atomic
Mutex
我的经验是,对于简单的计数器或标志位,总是优先考虑
sync/atomic
sync.Mutex
sync/atomic
sync/atomic
除了计数器,sync/atomic
布尔标志 (Boolean Flags): 你不能直接对
bool
uint32
int32
false
true
var initialized uint32 // 0: false, 1: true
func initOnce() {
if atomic.CompareAndSwapUint32(&initialized, 0, 1) {
// 只有第一个成功将initialized从0设为1的goroutine会执行这里
fmt.Println("执行初始化逻辑...")
} else {
fmt.Println("已经被初始化过了,跳过。")
}
}原子性地更新指针 (Pointers):
atomic.LoadPointer
atomic.StorePointer
atomic.CompareAndSwapPointer
unsafe.Pointer
type Config struct {
// ... 配置字段
}
var currentConfig unsafe.Pointer // 指向*Config类型
func updateConfig(newCfg *Config) {
atomic.StorePointer(¤tConfig, unsafe.Pointer(newCfg))
}
func getConfig() *Config {
return (*Config)(atomic.LoadPointer(¤tConfig))
}值交换 (Value Swapping):
atomic.SwapInt32
atomic.SwapInt64
var status int32 = 1 // 1: 运行中, 2: 暂停, 3: 停止
func pauseSystem() int32 {
// 将状态设置为2(暂停),并返回旧状态
oldStatus := atomic.SwapInt32(&status, 2)
fmt.Printf("系统从状态 %d 变为暂停\n", oldStatus)
return oldStatus
}实现乐观锁/CAS (Compare And Swap):
atomic.CompareAndSwapInt32
atomic.CompareAndSwapInt64
常见的陷阱:
对齐问题 (Alignment Issues): 这是最隐蔽也最危险的陷阱之一。在某些32位架构(如ARM)上,
int64
uint64
atomic
int64
uint64
// 推荐做法:将int64放在结构体开头
type SafeCounter struct {
value int64 // 确保64位对齐
// 其他字段...
}
// 潜在问题:在某些32位架构上可能不对齐
type UnsafeCounter struct {
otherField byte
value int64 // 如果otherField是1字节,value可能不是64位对齐
}混合使用原子操作和非原子操作: 一个非常常见的错误是,你可能用
atomic.AddInt64
fmt.Println(myCounter.value)
atomic.LoadInt64
sync/atomic
误用于复杂逻辑:
atomic
atomic
sync.Mutex
// 错误示例:试图用atomic解决复杂逻辑
var balance int64 = 100
var limit int64 = 50
func withdraw(amount int64) bool {
currentBalance := atomic.LoadInt64(&balance)
currentLimit := atomic.LoadInt64(&limit) // 假设limit也是原子变量
// 这里的判断和修改不是原子的整体
if currentBalance >= amount && currentLimit >= amount {
// 在这里,balance和limit可能已经被其他goroutine修改了
atomic.AddInt64(&balance, -amount)
atomic.AddInt64(&limit, -amount)
return true
}
return false
}
// 正确的做法可能需要一个Mutex来保护整个withdraw逻辑,或者一个复杂的CAS循环。atomic.Value
atomic.Value
总的来说,
sync/atomic
以上就是Golang使用sync/atomic原子操作实现安全计数的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号