Go 原子操做

概念

原子操做,意思就是執行的過程不能背終端的操做。在針對某個值的原子操做執行過程當中,cpu不會再去執行其餘針對這個值得操做。在底層,這會由CPU提供芯片級別的支持,因此絕對有效。即便在擁有多CPU核心,或者多CPU的計算機系統中,原子操做的保證也是不可撼動的。
Go語言提供了院子操做的包atomic。其中有不少函數能夠幫助咱們進行原子操做。可是隻能對幾種簡單類型進行原子操做:int3二、int6四、uint3二、uint6四、uintptr和unsafe.Ponter。atomic爲這些簡單類型童工了5中操做函數:增或減、比較並交換、載入、存儲和交換。數組

爲何選擇原子操做

咱們知道go語言在sync包中提供了鎖的包,可是爲何咱們還要使用atomic原子操做呢?總結下來有一下幾個緣由:安全

  • 加鎖的代價比較耗時,須要上下文切換。即便是在go語言的goroutine中也須要上下文的切換
  • 只是針對基本類型,可使用原子操做保證線程安全
  • 原子操做在用戶態能夠完成,性能比互斥鎖要高
  • 針對特定需求,原子操做的步驟簡單,不須要加鎖-操做-解鎖 這樣的步驟

五種操做

一下5中操做例子都是用uint64來寫併發

增或減

針對以上6種簡單類型,atomic包支持院子增/減的操做函數。函數

var i64 uint64
//第一個參數必須是指針
atomic.AddUint64(&i64,5)
//在uint類型中可使用^uint64(0)的方式打到減的效果
atomic.AddUint64(&i64, ^uint64(0))
fmt.Println(i64)

比較並交換(CAS-Compare And Swap)

var i64 uint64
    i64 = 5
    // cas接受3個參數,第一個爲須要替換值得指針,第二個爲舊值,第三個爲新值
    // 當指針指向的值,跟你傳遞的舊值相等的狀況下 指針指向的值會被替換
    ok := atomic.CompareAndSwapUint64(&i64,5, 50)
    fmt.Println(ok)
    // 當指針指向的值跟傳遞的舊值不相等,則返回false
    ok = atomic.CompareAndSwapUint64(&i64,40, 50)
    fmt.Println(ok)

載入

var i64 uint64
    i64 = 1
    //load 函數接收一個指針類型 返回指針指向的值
    num := atomic.LoadUint64(&i64)
    fmt.Println(num)

存儲

var i64 uint64
    i64 = 1
    //store 函數接受一個指針類型和一個值 函數將會把值賦到指針地址中
    atomic.StoreUint64(&i64, 5)
    fmt.Println(i64)

交換

var i64 uint64
    i64 = 1
    //swap接受一個指針 一個值。函數會把值賦給指針 並返回舊值
    old := atomic.SwapUint64(&i64, 5)
    fmt.Println("old:",old,"new:",i64)

原子值

原子值可接受的備操做值得類型不限,這意味着咱們能夠把任何類型的值放入原子值。原子值只有2個公開的方法:Load、Store。一個是獲取另外一個是存儲。
下面來看下簡單操做:性能

var countVal atomic.Value
    //store函數 接受interface 並存儲
    countVal.Store([]int{1,2,3,4,5})
    //load函數 返回 atomic.value中的值
    list := countVal.Load().([]int)

    fmt.Println(list)

下面有一個併發安全的int數組的例子ui

package main

import (
    "errors"
    "sync/atomic"
)

func main() {
    
}


// ConcurrentArray 表明併發安全的整數數組接口。
type ConcurrentArray interface {
    // Set 用於設置指定索引上的元素值。
    Set(index uint32, elem int) (err error)
    // Get 用於獲取指定索引上的元素值。
    Get(index uint32) (elem int, err error)
    // Len 用於獲取數組的長度。
    Len() uint32
}

type MyArray struct {
    val atomic.Value
    length uint32
}

func (array *MyArray)CheckValue()(err error){
    if array.val.Load() == nil{
        errors.New("array is empty")
    }
    return nil
}

func (array *MyArray)CheckIndex(index uint32)(error){
    if array.length <= index{
        errors.New("array out of the range")
    }

    return nil
}

func (m *MyArray)Set(index uint32, elem int)(err error){
    if err := m.CheckValue();err != nil{
        return err
    }

    if err = m.CheckIndex(index);err!=nil{
        return err
    }

    newArray := make([]int, m.length)
    copy(newArray ,m.val.Load().([]int))
    newArray[index] = elem
    m.val.Store(newArray)

    return nil
}

func (array *MyArray)Get(index uint32) (elem int, err error){
    if err := array.CheckValue();err != nil{
        return 0,err
    }

    if err = array.CheckIndex(index);err!=nil{
        return 0,err
    }

    num := array.val.Load().([]int)[index]

    return num, err
}
相關文章
相關標籤/搜索