Golang中的atomic(原子操作)包提供了一组用于进行低级别原子操作的函数,以确保在多个goroutine中对共享变量的读写操作是线程安全的。在并发程序中,有时需要对共享变量执行简单的读写操作,这些操作不能被打断或中断,否则可能导致数据竞争或内存泄漏等问题。在这种情况下,使用atomic包可以确保这些操作的原子性。
atomic包
提供的函数包括以下几种类型:
Add系列函数:用于在原子操作中增加或减少整型变量的值。
CompareAndSwap系列函数:用于比较并交换值,只有当变量的值与预期值相等时,才会交换。
Load系列函数:用于从原子操作中加载变量的值。
Store系列函数:用于在原子操作中存储变量的值。
Swap系列函数:用于在原子操作中交换变量的值。
使用atomic包时需要注意点:
atomic包中的操作只适用于整型变量和指针类型变量,对于其他类型的变量需要自己实现锁机制。
原子操作虽然可以保证操作的原子性,但是无法保证在多个goroutine中变量的执行顺序。
原子操作需要注意对变量的内存可见性,即当一个goroutine对变量进行原子操作时,其他goroutine需要保证能够读取到最新的变量值。
以下是atomic包的一些常见应用场景:
计数器:atomic包的Add函数可以在多个goroutine中安全地对计数器进行操作。
读写锁:使用atomic包中的CompareAndSwap函数可以实现一种基于自旋的读写锁。
单例模式:使用atomic包中的Once函数可以确保某个操作只执行一次,从而实现单例模式。
资源池:使用atomic包中的Swap函数可以实现一个简单的资源池,多个goroutine可以共享使用资源池中的资源。
下面是一个使用atomic包实现的简单计数器的示例:
package main
import (
"fmt"
"sync/atomic"
"time"
)
func main() {
var count int64
for i := 0; i < 10; i++ {
gofunc() {
for j := 0; j < 1000; j++ {
atomic.AddInt64(&count, 1)
}
}()
}
time.Sleep(time.Second)
fmt.Println("count:", count)
}
在上面的代码中,我们创建了一个计数器变量count,然后启动了10个goroutine来对其进行累加操作。由于使用了atomic包中的AddInt64函数,因此多个goroutine之间的累加操作是线程安全的