Go语言入门-原子操作

Go语言入门-原子操作

什么是原子操作

参考笔者博文 原子操作:CAS、TAS、TTAS、FAA浅析

Go语言中的atomic包

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)

示例演示

  • 示例1 进行FFA数据自增处理
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
 */
  • 示例2 通过互斥锁实现余额累加功能

// 增加余额
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
 */
  • 示例3 通过原子操作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
*/
  • 示例3 通过原子操作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 > 互斥锁。

  • 示例4 不同版本的计数器示例代码
    Go语言基础之并发-原子操作
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

 */

 

你可能感兴趣的:(GO语言)