参考笔者博文 原子操作:CAS、TAS、TTAS、FAA浅析
Atomaic包中提供了原子的操作包括:
数据读取类函数 LoadXXX()
数据写入类函数 StoreXXX()
数据自增(FFA)类函数 AddXXX()
数据交换类函数 SwapXXX()
比较并交换(CAS)函数 CompareAndSwapXXX()
// SwapInt32 atomically stores new into *addr and returns the previous *addr value.
func SwapInt32(addr *int32, new int32) (old int32)
// SwapInt64 atomically stores new into *addr and returns the previous *addr value.
func SwapInt64(addr *int64, new int64) (old int64)
// SwapUint32 atomically stores new into *addr and returns the previous *addr value.
func SwapUint32(addr *uint32, new uint32) (old uint32)
// SwapUint64 atomically stores new into *addr and returns the previous *addr value.
func SwapUint64(addr *uint64, new uint64) (old uint64)
// SwapUintptr atomically stores new into *addr and returns the previous *addr value.
func SwapUintptr(addr *uintptr, new uintptr) (old uintptr)
// SwapPointer atomically stores new into *addr and returns the previous *addr value.
func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer)
// CompareAndSwapInt32 executes the compare-and-swap operation for an int32 value.
func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
// CompareAndSwapInt64 executes the compare-and-swap operation for an int64 value.
func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool)
// CompareAndSwapUint32 executes the compare-and-swap operation for a uint32 value.
func CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool)
// CompareAndSwapUint64 executes the compare-and-swap operation for a uint64 value.
func CompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool)
// CompareAndSwapUintptr executes the compare-and-swap operation for a uintptr value.
func CompareAndSwapUintptr(addr *uintptr, old, new uintptr) (swapped bool)
// CompareAndSwapPointer executes the compare-and-swap operation for a unsafe.Pointer value.
func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)
// AddInt32 atomically adds delta to *addr and returns the new value.
func AddInt32(addr *int32, delta int32) (new int32)
// AddUint32 atomically adds delta to *addr and returns the new value.
// To subtract a signed positive constant value c from x, do AddUint32(&x, ^uint32(c-1)).
// In particular, to decrement x, do AddUint32(&x, ^uint32(0)).
func AddUint32(addr *uint32, delta uint32) (new uint32)
// AddInt64 atomically adds delta to *addr and returns the new value.
func AddInt64(addr *int64, delta int64) (new int64)
// AddUint64 atomically adds delta to *addr and returns the new value.
// To subtract a signed positive constant value c from x, do AddUint64(&x, ^uint64(c-1)).
// In particular, to decrement x, do AddUint64(&x, ^uint64(0)).
func AddUint64(addr *uint64, delta uint64) (new uint64)
// AddUintptr atomically adds delta to *addr and returns the new value.
func AddUintptr(addr *uintptr, delta uintptr) (new uintptr)
// LoadInt32 atomically loads *addr.
func LoadInt32(addr *int32) (val int32)
// LoadInt64 atomically loads *addr.
func LoadInt64(addr *int64) (val int64)
// LoadUint32 atomically loads *addr.
func LoadUint32(addr *uint32) (val uint32)
// LoadUint64 atomically loads *addr.
func LoadUint64(addr *uint64) (val uint64)
// LoadUintptr atomically loads *addr.
func LoadUintptr(addr *uintptr) (val uintptr)
// LoadPointer atomically loads *addr.
func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)
// StoreInt32 atomically stores val into *addr.
func StoreInt32(addr *int32, val int32)
// StoreInt64 atomically stores val into *addr.
func StoreInt64(addr *int64, val int64)
// StoreUint32 atomically stores val into *addr.
func StoreUint32(addr *uint32, val uint32)
// StoreUint64 atomically stores val into *addr.
func StoreUint64(addr *uint64, val uint64)
// StoreUintptr atomically stores val into *addr.
func StoreUintptr(addr *uintptr, val uintptr)
// StorePointer atomically stores val into *addr.
func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer)
func main() {
var a int32
var b uint32
// FFA fetch and add
atomic.AddInt32(&a, -10)
atomic.AddUint32(&b, 10)
fmt.Println(a)
fmt.Println(b)
}
/**
output:
-10
10
*/
// 增加余额
func addBalance(balance *int32, amount int32, wg *sync.WaitGroup, lock *sync.Mutex) {
lock.Lock()
// 锁住后增加余额
*balance = *balance + amount
lock.Unlock()
wg.Done()
}
func main() {
start := time.Now()
var balance int32
balance = int32(0)
count := 10000
wg := sync.WaitGroup{}
wg.Add(count)
var lock sync.Mutex
// 启动100000个协程同时处理增加余额
for i := 0; i < count; i++ {
go addBalance(&balance, 1, &wg, &lock)
}
// 此处打印balance会有问题,因为还有协程没有执行完,所以预期值有问题
fmt.Printf("balance = %d\n", balance)
// 确保子协程全部执行结束
wg.Wait()
end := time.Now()
fmt.Println(end.Sub(start).Seconds())
fmt.Printf("balance = %d\n", balance)
}
/**
output:
balance = 9946
0.0039637
balance = 10000
*/
CAS
实现余额累加功能// 增加余额
func addBalance(balance *int32, amount int32, wg *sync.WaitGroup) {
//循环会占用cpu
//原子操作
//会出现ABA的问题,ABA的问题对指针的处理影响较大 增加版本号
//不支持多个变量cas,使用dwCAS 把共享变量进行拆分
for {
old := atomic.LoadInt32(balance)
new := old + amount
if atomic.CompareAndSwapInt32(balance, old, new) {
break
}
}
wg.Done()
}
func main() {
start := time.Now()
var balance int32
balance = int32(0)
count := 10000
wg := sync.WaitGroup{}
wg.Add(count)
for i := 0; i < count; i++ {
go addBalance(&balance, 1, &wg)
}
// 此处打印balance会有问题,因为还有协程没有执行完,所以预期值有问题
fmt.Printf("balance = %d\n", balance)
// 确保子协程全部执行结束
wg.Wait()
end := time.Now()
fmt.Println(end.Sub(start).Seconds())
fmt.Printf("balance = %d\n", balance)
}
/**
balance = 9994
0.0029918
balance = 10000
*/
FFA
实现余额累加功能
// 增加余额
func addBalance(balance *int32, amount int32, wg *sync.WaitGroup) {
//原子操作
atomic.AddInt32(balance, amount)
wg.Done()
}
func main() {
start := time.Now()
var balance int32
balance = int32(0)
count := 10000
wg := sync.WaitGroup{}
wg.Add(count)
for i := 0; i < count; i++ {
go addBalance(&balance, 1, &wg)
}
// 此处打印balance会有问题,因为还有协程没有执行完,所以预期值有问题
fmt.Printf("balance = %d\n", balance)
// 确保子协程全部执行结束
wg.Wait()
end := time.Now()
fmt.Println(end.Sub(start).Seconds())
fmt.Printf("balance = %d\n", balance)
}
/**
balance = 9957
0.0019939
balance = 10000
*/
通过以上例子看出,大致的执行效率 原子操作FFA > 原子操作CAS > 互斥锁。
package main
import (
"fmt"
"sync"
"sync/atomic"
"time"
)
type Counter interface {
inc()
Get() int32
}
// 普通版本
type CommonCounter struct {
counter int32
}
func (c *CommonCounter) inc() {
c.counter++
}
func (c *CommonCounter) Get() int32 {
return c.counter
}
// 互斥版本
type MutexCounter struct {
counter int32
sync.RWMutex
}
func (m *MutexCounter) inc() {
m.Lock()
m.counter++
m.Unlock()
}
func (m *MutexCounter) Get() int32 {
var result int32
m.RLock()
result = m.counter
m.RUnlock()
return result
}
// 原子操作1 FAA
type AtomicCounter1 struct {
counter int32
}
func (a *AtomicCounter1) inc() {
atomic.AddInt32(&a.counter, 1)
}
func (a *AtomicCounter1) Get() int32 {
return atomic.LoadInt32(&a.counter)
}
// 原子操作CAS比较交换
type AtomicCounter2 struct {
counter int32
}
func (a *AtomicCounter2) inc() {
for {
old := atomic.LoadInt32(&a.counter)
new := old + 1
if atomic.CompareAndSwapInt32(&a.counter, old, new) {
break
}
}
}
func (a *AtomicCounter2) Get() int32 {
return atomic.LoadInt32(&a.counter)
}
func test (c Counter) {
var wg sync.WaitGroup
start := time.Now()
for i := 0; i < 5000; i++ {
wg.Add(1)
go func() {
c.inc()
wg.Done()
}()
}
wg.Wait()
end := time.Now()
fmt.Println(c.Get(), end.Sub(start).Seconds())
}
func main() {
// 无锁
c1 := CommonCounter{}
test(&c1)
// 有互斥锁
c2 := MutexCounter{}
test(&c2)
//FAA
c3 := AtomicCounter1{}
//CAS
test(&c3)
c4 := AtomicCounter2{}
test(&c4)
}
/**
output:
4835 0.0019187
5000 0.000997
5000 0.0019952
5000 0.0009972
*/