Golang Atomic原子操作

在Golang中,使用atomic包进行原子操作时,为什么有时候性能比Mutex更好?具体哪些场景适合用atomic而不是Mutex?atomic.Value和普通的atomic操作有什么区别?能否举例说明在实际项目中如何正确使用atomic来避免竞态条件?

2 回复

Golang的atomic包提供原子操作,用于并发安全地读写变量,避免数据竞争。常用方法如Add、Load、Store、Swap等,适用于计数器、标志位等场景。比互斥锁更轻量,但功能有限。

更多关于Golang Atomic原子操作的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go语言中,atomic包提供了底层的原子级内存操作,用于实现无锁的并发控制,避免数据竞争。它适用于对基本数据类型(如int32int64uint32等)进行简单的读写或修改操作,比使用互斥锁(sync.Mutex)更高效,但功能有限。

主要函数

  1. 加载(Load):原子性地读取值。
    • LoadInt32(addr *int32) int32
  2. 存储(Store):原子性地写入值。
    • StoreInt32(addr *int32, val int32)
  3. 加法(Add):原子性地增加值。
    • AddInt32(addr *int32, delta int32) int32
  4. 比较并交换(CompareAndSwap, CAS):仅在当前值等于旧值时更新为新值。
    • CompareAndSwapInt32(addr *int32, old, new int32) bool
  5. 交换(Swap):原子性地设置新值并返回旧值。
    • SwapInt32(addr *int32, new int32) int32

示例代码

以下是一个使用atomic实现计数器的简单示例:

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
)

func main() {
	var counter int32
	var wg sync.WaitGroup

	// 启动多个goroutine并发增加计数器
	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go func() {
			atomic.AddInt32(&counter, 1)
			wg.Done()
		}()
	}

	wg.Wait()
	fmt.Println("Final counter:", atomic.LoadInt32(&counter)) // 输出: Final counter: 1000
}

注意事项

  • 适用场景:仅用于简单的整数或指针操作。复杂数据结构需用sync.Mutexsync.RWMutex
  • 性能:在高度竞争环境下,atomic通常比锁更快,但非竞争时可能因内存屏障而稍慢。
  • 内存顺序:Go的atomic操作保证顺序一致性,无需手动管理内存屏障。

通过合理使用atomic,可以提升并发程序的性能和可靠性。

回到顶部