目录
sync/atomic 包
原子操作(Atomic Operations)是指在并发编程中,能够保证在多线程环境下对共享变量的操作是不可分割的、原子性的操作。它们能够避免竞态条件(Race Condition)和数据竞争(Data Race),确保操作的原子性和线程安全。
Go 语言的 sync/atomic
包提供了一些原子操作函数,常用的有以下几种:
Add
:原子地将指定的整数加到某个地址上,并返回更新后的值。
func AddInt32(addr *int32, delta int32) (new int32)
func AddInt64(addr *int64, delta int64) (new int64)
func AddUint32(addr *uint32, delta uint32) (new uint32)
func AddUint64(addr *uint64, delta uint64) (new uint64)
func AddUintptr(addr *uintptr, delta uintptr) (new uintptr)
Load
:原子地读取指定地址的值,并返回读取的值。
func LoadInt32(addr *int32) (val int32)
func LoadInt64(addr *int64) (val int64)
func LoadUint32(addr *uint32) (val uint32)
func LoadUint64(addr *uint64) (val uint64)
func LoadUintptr(addr *uintptr) (val uintptr)
Store
:原子地将指定值存储到某个地址上。
func StoreInt32(addr *int32, val int32)
func StoreInt64(addr *int64, val int64)
func StoreUint32(addr *uint32, val uint32)
func StoreUint64(addr *uint64, val uint64)
func StoreUintptr(addr *uintptr, val uintptr)
CompareAndSwap
:比较指定地址的值与旧值是否相等,如果相等则原子地将新值存储到该地址上,并返回是否进行了交换。
func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool)
func CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool)
func CompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool)
func CompareAndSwapUintptr(addr *uintptr, old, new uintptr) (swapped bool)
除了上述函数之外,还有许多其他原子操作函数,例如递增、递减、位操作等。这些原子操作函数都能够在并发环境中保证操作的原子性,避免竞态条件和数据竞争。
需要注意的是,原子操作只能用于基本类型和指针类型。对于复杂类型或者需要保证多个变量的一致性的操作,可以结合使用互斥锁(Mutex
)或读写锁(RWMutex
)来实现线程安全。
在并发编程中,多个线程或协程同时访问和修改共享变量时,可能会引发竞态条件(Race Condition)和数据竞争(Data Race)。竞态条件是指多个线程对同一个资源进行竞争操作,导致结果的正确性依赖于执行顺序的随机性;数据竞争是指多个线程对同一个变量进行读写操作,并且至少有一个操作是写入操作,从而导致内存访问冲突和未定义行为。
原子操作提供了一种方式来避免竞态条件和数据竞争,确保对共享变量的操作是不可分割的、原子性的操作。具体来说,原子操作具有以下几个重要的意义:
-
原子性操作:原子操作能够确保对共享变量的操作是不可分割的单元操作。在任何时刻,只有一个线程能够执行原子操作,其他线程需要等待原子操作完成后才能继续执行。这样可以避免多个线程同时修改共享变量导致的冲突和错误结果。
-
线程安全:原子操作是线程安全的,即使在高并发的情况下也能保证数据的正确性。通过原子操作能够保持共享变量的一致性,避免出现并发访问导致的数据不一致性和未定义行为。
-
同步机制:原子操作提供了一种轻量级的同步机制,不需要使用显式的锁(如互斥锁)来保护共享变量。相比于传统的锁机制,原子操作通常具有更低的开销和更高的性能。
-
并发安全性:原子操作是并发安全的,不需要额外的同步机制。在多线程或协程环境下,通过原子操作可以避免竞态条件和数据竞争,提供了一种简单而有效的方式来实现并发安全性。
总的来说,原子操作在并发编程中起到了保护共享变量的作用,避免了竞态条件和数据竞争,提供了一种高效和可靠的方式来处理多线程或协程之间的并发访问问题。在 Go 语言中,sync/atomic
包提供了一系列原子操作函数,方便开发者进行原子操作的使用和管理。
package main
import (
"fmt"
"sync"
"time"
)
var (
x int64
wg sync.WaitGroup
rwlock sync.RWMutex
)
func write() {
// lock.Lock() // 加互斥锁
rwlock.Lock() // 加写锁
x = x + 1
time.Sleep(10 * time.Millisecond) // 假设读操作耗时10毫秒
rwlock.Unlock() // 解写锁
// lock.Unlock() // 解互斥锁
wg.Done()
}
func read() {
// lock.Lock() // 加互斥锁
rwlock.RLock() // 加读锁
time.Sleep(time.Millisecond) // 假设读操作耗时1毫秒
rwlock.RUnlock() // 解读锁
// lock.Unlock() // 解互斥锁
wg.Done()
}
func main() {
start := time.Now()
for i := 0; i < 10; i++ {
wg.Add(1)
go write()
}
for i := 0; i < 1000; i++ {
wg.Add(1)
go read()
}
wg.Wait()
end := time.Now()
fmt.Println(end.Sub(start))
}