golang atomic看這篇就夠啦

atomic

atomic是go提供的一個(gè)執(zhí)行原子操作的包,雖然提供了這個(gè)包,但是go官方并不是很推薦使用;
除了做一些低級(jí)的應(yīng)用程序外,go更推薦使用通道和sync來(lái)處理;

PS: go中的sync.Mutex底層都是通過(guò)atomic來(lái)實(shí)現(xiàn)的;這么看atomic確實(shí)比較低級(jí)。

雖然它比較底層,但是我還是有必要了解使用它。

1. 什么是原子操作呢?

可以這么簡(jiǎn)單的理解,在程序執(zhí)行某一個(gè)操作的時(shí)候,在計(jì)算機(jī)底層其實(shí)是分了多個(gè)步驟去處理它;
有多個(gè)步驟處理,那么就意味著有中間狀態(tài)(操作中、沒(méi)操作完的狀態(tài));

而原子操作,它是一個(gè)不可分割的整體,沒(méi)有中間狀態(tài),要么成功了、要么失敗了。,它通過(guò)底層的cpu操作去實(shí)現(xiàn)。

這樣有什么好處,在多goroutine并發(fā)操作的同一個(gè)數(shù)據(jù)的時(shí)候,可以保護(hù)數(shù)據(jù)的一致性。

2. atomic 概述

在golang中,atomic主要提供了下面幾種操作:

  1. Store - 存一個(gè)值
  2. Load - 獲取一個(gè)值
  3. Swap - 更新一個(gè)值(返回舊值)
  4. Add - 加值
  5. CompareAndSwap - 比較然后更新值(如果值還是原來(lái)的值,則更新)

另外主要是對(duì)下面的類(lèi)型實(shí)現(xiàn)原子操作:

  • int32
  • int64
  • uint32
  • unint64
  • uintptr

函數(shù)格式為操作 + 類(lèi)型,比如:
對(duì)于int32,會(huì)有下列函數(shù):

  • atomic.AddInt32()
  • atomic.StoreInt32()
  • atomic.LoadInt32()
  • atomic.SwapInt32()
  • atomic.CompareAndSwapInt32(),其它類(lèi)型同理。

雖然go提供了函數(shù)操作,但是go鼓勵(lì)我們采用,方法調(diào)用(更人性化、不臃腫).
比如atomic.AddInt32()對(duì)應(yīng)的方法為Add()非常簡(jiǎn)潔哦~

3. 函數(shù)調(diào)用方式

需要注意的是:atomic操作的都是地址
由于每種類(lèi)型使用方式基本差不多,這里拿int32舉例演示

package main

import (
    "fmt"
    "sync/atomic"
)

func main() {
    var a int32

    // 將1 存給a變量
    atomic.StoreInt32(&a, 1)

    fmt.Println(a)
    fmt.Println(atomic.LoadInt32(&a)) // 讀取a地址的值

    // 更新值
    oldValue := atomic.SwapInt32(&a, 2)
    fmt.Println("新值:", a, "舊值:", oldValue)

    // 添加值
    atomic.AddInt32(&a, 2) // 加2
    fmt.Println("增加后值:", a)
    atomic.AddInt32(&a, -1) // 減1
    fmt.Println("減少后值:", a)

    // 比較后更新 返回是否更新成功
    swapped := atomic.CompareAndSwapInt32(&a, 3, 6) // 如果舊值是3 則更新為6
    fmt.Println("第一次比較更新是否成功:", swapped, "當(dāng)前值為:", a)

    swapped = atomic.CompareAndSwapInt32(&a, 4, 3) // 如果舊值是4 則更新為3
    fmt.Println("第二次比較更新是否成功: ", swapped, "當(dāng)前值為:", a)
}

// 1
// 1
// 新值: 2 舊值: 1
// 增加后值: 4
// 減少后值: 3
// 第一次比較更新是否成功: true 當(dāng)前值為: 6
// 第二次比較更新是否成功:  false 當(dāng)前值為: 6

4. 方法調(diào)用方式

go推薦使用這種,簡(jiǎn)潔。

直接將函數(shù)調(diào)用方式改成方法調(diào)用方式,如下:

package main

import (
    "fmt"
    "sync/atomic"
)

func main() {
    // 使用atomic內(nèi)置的類(lèi)型(結(jié)構(gòu)體)
    // 注意這個(gè)時(shí)候取值 要用a.Load()方式
    var a atomic.Int32

    // 將1 存給a變量
    a.Store(1)

    fmt.Println(a.Load()) // 讀取a地址的值

    // 更新值
    oldValue := a.Swap(2)
    fmt.Println("新值:", a.Load(), "舊值:", oldValue)

    // 添加值
    a.Add(2) // 加2
    fmt.Println("增加后值:", a.Load())
    a.Add(-1) // 減1
    fmt.Println("減少后值:", a.Load())

    // 比較后更新 返回是否更新成功
    swapped := a.CompareAndSwap(3, 6) // 如果舊值是3 則更新為6
    fmt.Println("第一次比較更新是否成功:", swapped, "當(dāng)前值為:", a.Load())

    swapped = a.CompareAndSwap(4, 3) // 如果舊值是4 則更新為3
    fmt.Println("第二次比較更新是否成功: ", swapped, "當(dāng)前值為:", a.Load())
}

看看是不是簡(jiǎn)介了很多。

5. 無(wú)符號(hào)類(lèi)型做減法

對(duì)于有符號(hào)類(lèi)型,Add時(shí)候直接給一個(gè)負(fù)數(shù)是沒(méi)有問(wèn)題的;但是對(duì)于無(wú)符號(hào)類(lèi)型比如(uint32,uint64)不能直接給負(fù)數(shù)——無(wú)符號(hào)沒(méi)有負(fù)數(shù),這個(gè)時(shí)候需要轉(zhuǎn)換下,

怎么轉(zhuǎn)換下呢?
在A(yíng)dd的時(shí)候,加一個(gè)按位取反,然后+1的數(shù)

下面看代碼:

package main

import (
    "fmt"
    "sync/atomic"
)

func main() {
    // 無(wú)符號(hào)類(lèi)型整數(shù)
    var a atomic.Uint32

    // 將1 存給a變量
    a.Store(10)

    fmt.Println(a.Load()) // 讀取a地址的值

    // 添加值
    a.Add(2)                       // 加2
    fmt.Println("增加后值:", a.Load()) // 加完后當(dāng)前為 12
    a.Add(^uint32(3) + 1)          // 減3 按位取反 然后加1

    fmt.Println("第一次減少后值:", a.Load())

    // 減少1
    a.Add(^uint32(1) + 1)
    fmt.Println("第二次減少后值: ", a.Load())

    // 再次減少1
    a.Add(^uint32(0)) // ^uint32(0) 等價(jià)于 ^uint32(1) + 1
    fmt.Println("第三次減少后值: ", a.Load())
}

// 10
// 增加后值: 12
// 第一次減少后值: 9
// 第二次減少后值:  8
// 第三次減少后值:  7

6. Value類(lèi)型(任意類(lèi)型)

前面的操作都是針對(duì)某個(gè)類(lèi)型的操作,這個(gè)類(lèi)型可以對(duì)任意類(lèi)型操作
PS: 這種類(lèi)型,沒(méi)有Add方式

package main

import (
    "fmt"
    "sync/atomic"
)

type Person struct {
    name string
    age  int8
}

func main() {
    // 任意類(lèi)型
    var a atomic.Value

    p := Person{
        name: "張三",
        age:  18,
    }
    // 將p 結(jié)構(gòu)體存入
    a.Store(p)

    fmt.Printf("取出值為:%#v\n", a.Load()) // 讀取a地址的值
}

// 取出值為:main.Person{name:"張三", age:18}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容