Go
一个或者多个操作在 CPU 执行的过程中不被中断的特性,称为原子性(atomicity) 。这些操作对外表现成一个不可分割的整体,他们要么都执行,要么都不执行,外界不会看到他们只执行到一半的状态。
CPU
ACID
Go 语言提供了哪些原子操作
Gosync/atomic
AddXXXTypeint32int64uint32uint64uintptrXXXTypeLoadXXXTypePointerStoreCASGoCAS
互斥锁跟原子操作的区别
syncMutexatomic
Mutexatomiclock-freeCPU
对于一个变量更新的保护,原子操作通常会更有效率,并且更能利用计算机多核的优势。
比如下面这个,使用互斥锁的并发计数器程序:
func mutexAdd() {
var a int32 = 0
var wg sync.WaitGroup
var mu sync.Mutex
start := time.Now()
for i := 0; i < 100000000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
mu.Lock()
a += 1
mu.Unlock()
}()
}
wg.Wait()
timeSpends := time.Now().Sub(start).Nanoseconds()
fmt.Printf("use mutex a is %d, spend time: %v\n", a, timeSpends)
}
Mutexatomic.AddInt32(&a, 1)
func AtomicAdd() {
var a int32 = 0
var wg sync.WaitGroup
start := time.Now()
for i := 0; i < 1000000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
atomic.AddInt32(&a, 1)
}()
}
wg.Wait()
timeSpends := time.Now().Sub(start).Nanoseconds()
fmt.Printf("use atomic a is %d, spend time: %v\n", atomic.LoadInt32(&a), timeSpends)
}
1000000
需要注意的是,所有原子操作方法的被操作数形参必须是指针类型,通过指针变量可以获取被操作数在内存中的地址,从而施加特殊的CPU指令,确保同一时间只有一个goroutine能够进行操作。
CAS
比较并交换
CASCompareAndSwap
func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)
oldCAS
CASfor
int32unsafe.PointerCAS
unsafe.Pointer提供了绕过Go语言指针类型限制的方法,unsafe指的并不是说不安全,而是说官方并不保证向后兼容。
// 定义一个struct类型P
type P struct{ x, y, z int }
// 执行类型P的指针
var pP *P
func main() {
// 定义一个执行unsafe.Pointer值的指针变量
var unsafe1 = (*unsafe.Pointer)(unsafe.Pointer(&pP))
// Old pointer
var sy P
// 为了演示效果先将unsafe1设置成Old Pointer
px := atomic.SwapPointer(
unsafe1, unsafe.Pointer(&sy))
// 执行CAS操作,交换成功,结果返回true
y := atomic.CompareAndSwapPointer(
unsafe1, unsafe.Pointer(&sy), px)
fmt.Println(y)
}
CASOld Pointer
MutexCASatomicsync
Mutexstate
type Mutex struct {
state int32
sema uint32
}
sync.MutexLock
func (m *Mutex) Lock() {
// Fast path: grab unlocked mutex.
if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
if race.Enabled {
race.Acquire(unsafe.Pointer(m))
}
return
}
// Slow path (outlined so that the fast path can be inlined)
m.lockSlow()
}
atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked)m.stateCASm.state==0mutexLocked
atomic.Value保证任意值的读写安全
atomicStore
func StoreInt32(addr *int32, val int32)
func StoreInt64(addr *int64, val int64)
func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer)
...
这些操作方法的定义与上面介绍的那些操作的方法类似,我就不再演示怎么使用这些方法了。
StorePointeratomicatomic.Valueunsafe.Pointer
atomic.Valueunsafe.Pointer
atomic.Value
v.Store(c)catomic.Valuevc := v.Load()v
SwapCompareAndSwap
Load()Store()
Load()interface{}atomic.Value
type Rectangle struct {
length int
width int
}
var rect atomic.Value
func update(width, length int) {
rectLocal := new(Rectangle)
rectLocal.width = width
rectLocal.length = length
rect.Store(rectLocal)
}
func main() {
wg := sync.WaitGroup{}
wg.Add(10)
// 10 个协程并发更新
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()
update(i, i+5)
}()
}
wg.Wait()
_r := rect.Load().(*Rectangle)
fmt.Printf("rect.width=%d\nrect.length=%d\n", _r.width, _r.length)
}
atomic.ValueRectange
总结
atomicatomicatomic
atomic.Value