最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解Golang五種原子性操作的用法

 更新時間:2021年09月22日 15:37:03   作者:kevinyan  
本文主要介紹了詳解Golang五種原子性操作的用法,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下

本文我們詳細聊一下Go語言的原子操作的用法,啥是原子操作呢?顧名思義,原子操作就是具備原子性的操作... 是不是感覺說了跟沒說一樣,原子性的解釋如下:

一個或者多個操作在 CPU 執(zhí)行的過程中不被中斷的特性,稱為原子性(atomicity) 。這些操作對外表現(xiàn)成一個不可分割的整體,他們要么都執(zhí)行,要么都不執(zhí)行,外界不會看到他們只執(zhí)行到一半的狀態(tài)。

CPU執(zhí)行一系列操作時不可能不發(fā)生中斷,但如果我們在執(zhí)行多個操作時,能讓他們的中間狀態(tài)對外不可見,那我們就可以宣稱他們擁有了"不可分割”的原子性。
類似的解釋我們在數(shù)據(jù)庫事務(wù)的ACID概念里也聽過,只不過這里保障原子性的執(zhí)行體是CPU。

Go 語言提供了哪些原子操作

Go語言通過內(nèi)置包sync/atomic提供了對原子操作的支持,其提供的原子操作有以下幾大類:

  • 增減,操作方法的命名方式為AddXXXType,保證對操作數(shù)進行原子的增減,支持的類型為int32、int64、uint32、uint64、uintptr,使用時以實際類型替換前面我說的XXXType就是對應(yīng)的操作方法。
  • 載入,保證了讀取到操作數(shù)前沒有其他任務(wù)對它進行變更,操作方法的命名方式為LoadXXXType,支持的類型除了基礎(chǔ)類型外還支持Pointer,也就是支持載入任何類型的指針。
  • 存儲,有載入了就必然有存儲操作,這類操作的方法名以Store開頭,支持的類型跟載入操作支持的那些一樣。
  • 比較并交換,也就是CAS (Compare And Swap),像Go的很多并發(fā)原語實現(xiàn)就是依賴的CAS操作,同樣是支持上面列的那些類型。
  • 交換,這個簡單粗暴一些,不比較直接交換,這個操作很少會用。

互斥鎖跟原子操作的區(qū)別

平日里,在并發(fā)編程里,Go語言sync包里的同步原語Mutex是我們經(jīng)常用來保證并發(fā)安全的,那么他跟atomic包里的這些操作有啥區(qū)別呢?在我看來他們在使用目的和底層實現(xiàn)上都不一樣:

  • 使用目的:互斥鎖是用來保護一段邏輯,原子操作用于對一個變量的更新保護。
  • 底層實現(xiàn):Mutex由操作系統(tǒng)的調(diào)度器實現(xiàn),而atomic包中的原子操作則由底層硬件指令直接提供支持,這些指令在執(zhí)行的過程中是不允許中斷的,因此原子操作可以在lock-free的情況下保證并發(fā)安全,并且它的性能也能做到隨CPU個數(shù)的增多而線性擴展。

對于一個變量更新的保護,原子操作通常會更有效率,并且更能利用計算機多核的優(yōu)勢。

比如下面這個,使用互斥鎖的并發(fā)計數(shù)器程序:

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)
}

把Mutex改成用方法atomic.AddInt32(&a, 1)調(diào)用,在不加鎖的情況下仍然能確保對變量遞增的并發(fā)安全。

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)
}

可以在本地運行以上這兩段代碼,可以觀察到計數(shù)器的結(jié)果都最后都是1000000,都是線程安全的。
需要注意的是,所有原子操作方法的被操作數(shù)形參必須是指針類型,通過指針變量可以獲取被操作數(shù)在內(nèi)存中的地址,從而施加特殊的CPU指令,確保同一時間只有一個goroutine能夠進行操作。
上面的例子除了增加操作外我們還演示了載入操作,接下來我們來看一下CAS操作。

比較并交換

該操作簡稱CAS (Compare And Swap)。 這類操作的前綴為 CompareAndSwap :

func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)

func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)

該操作在進行交換前首先確保被操作數(shù)的值未被更改,即仍然保存著參數(shù) old 所記錄的值,滿足此前提條件下才進行交換操作。CAS的做法類似操作數(shù)據(jù)庫時常見的樂觀鎖機制。

需要注意的是,當有大量的goroutine 對變量進行讀寫操作時,可能導(dǎo)致CAS操作無法成功,這時可以利用for循環(huán)多次嘗試。

上面我只列出了比較典型的int32和unsafe.Pointer類型的CAS方法,主要是想說除了讀數(shù)值類型進行比較交換,還支持對指針進行比較交換。

unsafe.Pointer提供了繞過Go語言指針類型限制的方法,unsafe指的并不是說不安全,而是說官方并不保證向后兼容。

// 定義一個struct類型P
type P struct{ x, y, z int }
  
// 執(zhí)行類型P的指針
var pP *P
  
func main() {
  
    // 定義一個執(zhí)行unsafe.Pointer值的指針變量
    var unsafe1 = (*unsafe.Pointer)(unsafe.Pointer(&pP))
  
    // Old pointer
    var sy P
  
    // 為了演示效果先將unsafe1設(shè)置成Old Pointer
    px := atomic.SwapPointer(
        unsafe1, unsafe.Pointer(&sy))
  
    // 執(zhí)行CAS操作,交換成功,結(jié)果返回true
    y := atomic.CompareAndSwapPointer(
        unsafe1, unsafe.Pointer(&sy), px)
  
    fmt.Println(y)
}

上面的示例并不是在并發(fā)環(huán)境下進行的CAS,只是為了演示效果,先把被操作數(shù)設(shè)置成了Old Pointer。
其實Mutex的底層實現(xiàn)也是依賴原子操作中的CAS實現(xiàn)的,原子操作的atomic包相當于是sync包里的那些同步原語的實現(xiàn)依賴。

比如互斥鎖Mutex的結(jié)構(gòu)里有一個state字段,其是表示鎖狀態(tài)的狀態(tài)位。

type Mutex struct {
 state int32
 sema  uint32
}

為了方便理解,我們在這里將它的狀態(tài)定義為0和1,0代表目前該鎖空閑,1代表已被加鎖,以下是sync.Mutex中Lock方法的部分實現(xiàn)代碼。

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.state代表鎖的狀態(tài),通過CAS方法,判斷鎖此時的狀態(tài)是否空閑(m.state==0),是,則對其加鎖(mutexLocked常量的值為1)。

atomic.Value保證任意值的讀寫安全

atomic包里提供了一套Store開頭的方法,用來保證各種類型變量的并發(fā)寫安全,避免其他操作讀到了修改變量過程中的臟數(shù)據(jù)。

func StoreInt32(addr *int32, val int32)

func StoreInt64(addr *int64, val int64)

func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer)

 
...

這些操作方法的定義與上面介紹的那些操作的方法類似,我就不再演示怎么使用這些方法了。

值得一提的是如果你想要并發(fā)安全的設(shè)置一個結(jié)構(gòu)體的多個字段,除了把結(jié)構(gòu)體轉(zhuǎn)換為指針,通過StorePointer設(shè)置外,還可以使用atomic包后來引入的atomic.Value,它在底層為我們完成了從具體指針類型到unsafe.Pointer之間的轉(zhuǎn)換。

有了atomic.Value后,它使得我們可以不依賴于不保證兼容性的unsafe.Pointer類型,同時又能將任意數(shù)據(jù)類型的讀寫操作封裝成原子性操作(中間狀態(tài)對外不可見)。

atomic.Value類型對外暴露了兩個方法:

  • v.Store(c) - 寫操作,將原始的變量c存放到一個atomic.Value類型的v里。
  • c := v.Load() - 讀操作,從線程安全的v中讀取上一步存放的內(nèi)容。

1.17 版本我看還增加了Swap和CompareAndSwap方法。

簡潔的接口使得它的使用也很簡單,只需將需要做并發(fā)保護的變量讀取和賦值操作用Load()和Store()代替就行了。
由于Load()返回的是一個interface{}類型,所以在使用前我們記得要先轉(zhuǎn)換成具體類型的值,再使用。下面是一個簡單的

例子演示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 個協(xié)程并發(fā)更新
 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.Value,直接給Rectange類型的指針變量賦值,看看在并發(fā)條件下,兩個字段的值是不是能跟預(yù)期的一樣變成10和15。

總結(jié)

本文詳細介紹了Go語言原子操作atomic包中會被高頻使用的操作的使用場景和用法,當然我并沒有羅列atomic包里所有操作的用法,主要是考慮到有的用到的地方實在不多,或者是已經(jīng)被更好的方式替代,還有就是覺得確實沒必要,看完本文的內(nèi)容相信你已經(jīng)完全具備自行探索atomic包的能力了。

再強調(diào)一遍,原子操作由底層硬件支持,而鎖則由操作系統(tǒng)的調(diào)度器實現(xiàn)。鎖應(yīng)當用來保護一段邏輯,對于一個變量更新的保護,原子操作通常會更有效率,并且更能利用計算機多核的優(yōu)勢,如果要更新的是一個復(fù)合對象,則應(yīng)當使用atomic.Value封裝好的實現(xiàn)。

到此這篇關(guān)于詳解Golang五種原子性操作的用法的文章就介紹到這了,更多相關(guān)詳解Golang五種原子性操作的用法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

相關(guān)文章

  • 深入剖析Go語言中數(shù)組和切片的區(qū)別

    深入剖析Go語言中數(shù)組和切片的區(qū)別

    本文將深入探討 Go 語言數(shù)組和切片的區(qū)別,包括它們的定義、內(nèi)存布局、長度和容量、初始化和操作等方面。從而更好地在實際開發(fā)中選擇和使用合適的數(shù)據(jù)結(jié)構(gòu),提高代碼的效率和可維護性,需要的可以參考一下
    2023-05-05
  • Golang打包配置文件的實現(xiàn)示例

    Golang打包配置文件的實現(xiàn)示例

    本文主要介紹了Golang打包配置文件的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • Go語言的反射reflect使用大全

    Go語言的反射reflect使用大全

    Go語言中reflect包提供了運行時反射的功能,本文主要介紹了Go語言的反射reflect使用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-08-08
  • Go文件操作(新建打開寫入讀取刪除關(guān)閉)學(xué)習(xí)筆記

    Go文件操作(新建打開寫入讀取刪除關(guān)閉)學(xué)習(xí)筆記

    這篇文章主要為大家介紹了Go文件操作(新建打開寫入讀取刪除關(guān)閉)學(xué)習(xí)筆記,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2024-01-01
  • 分布式架構(gòu)在Go語言網(wǎng)站的應(yīng)用

    分布式架構(gòu)在Go語言網(wǎng)站的應(yīng)用

    分布式架構(gòu)是目前應(yīng)對高流量、高并發(fā)的重要解決方案,分布式架構(gòu)的核心思想是分而治之,將單臺服務(wù)器的資源劃分為多臺服務(wù)器進行協(xié)同完成,分布式架構(gòu)應(yīng)用于Go語言網(wǎng)站中既能提升服務(wù)速度,又能降低了服務(wù)器宕機的風(fēng)險
    2024-01-01
  • Golang 實現(xiàn)超大文件讀取的兩種方法

    Golang 實現(xiàn)超大文件讀取的兩種方法

    這篇文章主要介紹了Golang 實現(xiàn)超大文件讀取的兩種方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • golang?gorm學(xué)習(xí)之如何指定數(shù)據(jù)表

    golang?gorm學(xué)習(xí)之如何指定數(shù)據(jù)表

    在sql中首先要指定是從哪張表中查詢,所以這篇文章小編就來帶大家一起看一下gorm是如何根據(jù)model來自動解析表名的,感興趣的小伙伴可以了解下
    2023-08-08
  • 解析Go?中的?rune?類型

    解析Go?中的?rune?類型

    rune類型是?Go?語言的一種特殊數(shù)字類型,Go?語言通過rune處理中文,支持國際化多語言,本文給大家介紹Go?中的?rune?類型,感興趣的朋友一起看看吧
    2022-03-03
  • Go到底能不能實現(xiàn)安全的雙檢鎖(推薦)

    Go到底能不能實現(xiàn)安全的雙檢鎖(推薦)

    這篇文章主要介紹了Go到底能不能實現(xiàn)安全的雙檢鎖,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-05-05
  • Go語言創(chuàng)建、初始化數(shù)組的常見方式匯總

    Go語言創(chuàng)建、初始化數(shù)組的常見方式匯總

    這篇文章主要介紹了Go語言創(chuàng)建、初始化數(shù)組的常見方式,實例匯總了Go語言操作數(shù)組的常見技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-02-02

最新評論

中宁县| 文昌市| 班玛县| 定远县| 平邑县| 商洛市| 莱阳市| 荃湾区| 泽普县| 肥城市| 湟中县| 曲阜市| 德令哈市| 陕西省| 正镶白旗| 富顺县| 铁力市| 南澳县| 新沂市| 和龙市| 长春市| 南宁市| 磐石市| 泰兴市| 锦州市| 丽江市| 沙洋县| 疏附县| 苍梧县| 盱眙县| 卫辉市| 邹城市| 桐城市| 牟定县| 大冶市| 丰城市| 绥芬河市| 从江县| 社会| 红河县| 方正县|