成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

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

開發(fā) 前端
本文我們?cè)敿?xì)聊一下 Go 語言的原子操作的用法,啥是原子操作呢?顧名思義,原子操作就是具備原子性的操作...

 [[425030]]

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

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

CPU 執(zhí)行一系列操作時(shí)不可能不發(fā)生中斷,但如果我們?cè)趫?zhí)行多個(gè)操作時(shí),能讓他們的 中間狀態(tài)對(duì)外不可見 ,那我們就可以宣稱他們擁有了"不可分割”的原子性。

類似的解釋我們?cè)跀?shù)據(jù)庫事務(wù)的 ACID 概念里也聽過。

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

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

  •  
    1. AddXXXType 
    2. int32 
    3. int64 
    4. uint32 
    5. uint64 
    6. uintptr 
    7. XXXType 
  • 載入,保證了讀取到操作數(shù)前沒有其他任務(wù)對(duì)它進(jìn)行變更,操作方法的命名方式為 LoadXXXType ,支持的類型除了基礎(chǔ)類型外還支持 Pointer ,也就是支持載入任何類型的指針。
  • 存儲(chǔ),有載入了就必然有存儲(chǔ)操作,這類操作的方法名以 Store 開頭,支持的類型跟載入操作支持的那些一樣。
  •  
    1. CAS 
    2. Go 
    3. CAS 
  • 交換,這個(gè)簡單粗暴一些,不比較直接交換,這個(gè)操作很少會(huì)用。

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

平日里,在并發(fā)編程里,Go語言 sync 包里的同步原語 Mutex 是我們經(jīng)常用來保證并發(fā)安全的,那么他跟 atomic 包里的這些操作有啥區(qū)別呢?在我看來他們?cè)谑褂媚康暮偷讓訉?shí)現(xiàn)上都不一樣:

  • 使用目的:互斥鎖是用來保護(hù)一段邏輯,原子操作用于對(duì)一個(gè)變量的更新保護(hù)。

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

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

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

  1. func mutexAdd() { 
  2.  var a int32 =  0 
  3.  var wg sync.WaitGroup 
  4.  var mu sync.Mutex 
  5.  start := time.Now() 
  6.  for i := 0; i < 100000000; i++ { 
  7.   wg.Add(1
  8.   go func() { 
  9.    defer wg.Done() 
  10.    mu.Lock() 
  11.    a += 1 
  12.    mu.Unlock() 
  13.   }() 
  14.  } 
  15.  wg.Wait() 
  16.  timeSpends := time.Now().Sub(start).Nanoseconds() 
  17.  fmt.Printf("use mutex a is %d, spend time: %v\n", a, timeSpends) 

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

  1. func AtomicAdd() { 
  2.  var a int32 =  0 
  3.  var wg sync.WaitGroup 
  4.  start := time.Now() 
  5.  for i := 0; i < 1000000; i++ { 
  6.   wg.Add(1
  7.   go func() { 
  8.    defer wg.Done() 
  9.    atomic.AddInt32(&a, 1
  10.   }() 
  11.  } 
  12.  wg.Wait() 
  13.  timeSpends := time.Now().Sub(start).Nanoseconds() 
  14.  fmt.Printf("use atomic a is %d, spend time: %v\n", atomic.LoadInt32(&a), timeSpends) 

可以在本地運(yùn)行以上這兩段代碼,可以觀察到計(jì)數(shù)器的結(jié)果都最后都是 1000000 ,都是線程安全的。

需要注意的是,所有原子操作方法的被操作數(shù)形參必須是指針類型,通過指針變量可以獲取被操作數(shù)在內(nèi)存中的地址,從而施加特殊的CPU指令,確保同一時(shí)間只有一個(gè)goroutine能夠進(jìn)行操作。

上面的例子除了增加操作外我們還演示了載入操作,接下來我們來看一下 CAS 操作。

比較并交換

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

  1. func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool) 
  2.  
  3. func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool) 

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

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

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

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

  1. // 定義一個(gè)struct類型P 
  2. type P struct{ x, y, z int } 
  3.    
  4. // 執(zhí)行類型P的指針 
  5. var pP *P 
  6.    
  7. func main() { 
  8.    
  9.     // 定義一個(gè)執(zhí)行unsafe.Pointer值的指針變量 
  10.     var unsafe1 = (*unsafe.Pointer)(unsafe.Pointer(&pP)) 
  11.    
  12.     // Old pointer 
  13.     var sy P 
  14.    
  15.     // 為了演示效果先將unsafe1設(shè)置成Old Pointer 
  16.     px := atomic.SwapPointer( 
  17.         unsafe1, unsafe.Pointer(&sy)) 
  18.    
  19.     // 執(zhí)行CAS操作,交換成功,結(jié)果返回true 
  20.     y := atomic.CompareAndSwapPointer( 
  21.         unsafe1, unsafe.Pointer(&sy), px) 
  22.    
  23.     fmt.Println(y) 

上面的示例并不是在并發(fā)環(huán)境下進(jìn)行的 CAS ,只是為了演示效果,先把被操作數(shù)設(shè)置成了 Old Pointer 。

其實(shí) Mutex 的底層實(shí)現(xiàn)也是依賴原子操作中的 CAS 實(shí)現(xiàn)的,原子操作的 atomic 包相當(dāng)于是 sync 包里的那些同步原語的實(shí)現(xiàn)依賴。

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

  1. type Mutex struct { 
  2.  state int32 
  3.  sema  uint32 

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

  1. func (m *Mutex) Lock() { 
  2.    // Fast path: grab unlocked mutex. 
  3.    if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) { 
  4.        if race.Enabled { 
  5.            race.Acquire(unsafe.Pointer(m)) 
  6.        } 
  7.        return 
  8.    } 
  9.    // Slow path (outlined so that the fast path can be inlined) 
  10.     m.lockSlow() 

在 atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) 中, m.state 代表鎖的狀態(tài),通過 CAS 方法,判斷鎖此時(shí)的狀態(tài)是否空閑( m.state==0 ),是,則對(duì)其加鎖( mutexLocked 常量的值為1)。

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

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

  1. func StoreInt32(addr *int32, val int32) 
  2.  
  3. func StoreInt64(addr *int64, val int64) 
  4.  
  5. func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer) 
  6.  
  7. ... 

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

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

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

atomic.Value 類型對(duì)外暴露了兩個(gè)方法:

  •  
    1. v.Store(c) 
    2. atomic.Value 
  • c := v.Load() - 讀操作,從線程安全的 v 中讀取上一步存放的內(nèi)容。

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

簡潔的接口使得它的使用也很簡單,只需將需要做并發(fā)保護(hù)的變量讀取和賦值操作用 Load() 和 Store() 代替就行了。

由于 Load() 返回的是一個(gè) interface{} 類型,所以在使用前我們記得要先轉(zhuǎn)換成具體類型的值,再使用。下面是一個(gè)簡單的例子演示 atomic.Value 的用法。

  1. type Rectangle struct { 
  2.  length int 
  3.  width  int 
  4.  
  5. var rect atomic.Value 
  6.  
  7. func update(width, length int) { 
  8.  rectLocal := new(Rectangle) 
  9.  rectLocal.width = width 
  10.  rectLocal.length = length 
  11.  rect.Store(rectLocal) 
  12.  
  13. func main() { 
  14.  wg := sync.WaitGroup{} 
  15.  wg.Add(10
  16.  // 10 個(gè)協(xié)程并發(fā)更新 
  17.  for i := 0; i < 10; i++ { 
  18.   go func() { 
  19.    defer wg.Done() 
  20.    update(i, i+5
  21.   }() 
  22.  } 
  23.  wg.Wait() 
  24.  _r := rect.Load().(*Rectangle) 
  25.  fmt.Printf("rect.width=%d\nrect.length=%d\n", _r.width, _r.length) 

你也可以試試,不用 atomic.Value ,直接給 Rectange 類型的指針變量賦值,看看在并發(fā)條件下,兩個(gè)字段的值是不是能跟預(yù)期的一樣變成10和15。

總結(jié)

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

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

給網(wǎng)管個(gè)星標(biāo)第一時(shí)間吸我的知識(shí) :point_up_2:

責(zé)任編輯:張燕妮 來源: 網(wǎng)管叨bi叨
相關(guān)推薦

2023-06-27 08:45:19

原子操作Golang

2012-05-23 12:49:58

Java自增操作原子性

2023-01-05 12:30:32

Redis

2025-01-13 00:00:00

MapStruct枚舉映射

2021-05-16 17:14:30

線程安全性

2021-01-12 07:39:48

線程線程安全

2014-01-09 09:45:41

原子飛原子

2021-05-06 19:20:05

Java內(nèi)存模型

2021-06-02 16:30:33

PolarDB原子性數(shù)據(jù)庫

2024-01-15 00:11:04

Docker網(wǎng)絡(luò)系統(tǒng)

2022-10-13 14:14:42

開發(fā)微服務(wù)測試

2021-06-03 14:00:35

PolarDB

2023-09-22 11:58:49

2023-07-04 08:41:08

Redis數(shù)據(jù)類型

2024-05-20 12:00:00

Python列表推導(dǎo)式

2023-12-04 16:25:58

2009-02-17 10:40:26

頁面跳轉(zhuǎn)JSP教程

2013-05-23 09:49:28

虛擬化桌面虛擬化

2021-09-13 09:43:50

存儲(chǔ)技術(shù)存儲(chǔ)軟件定義存儲(chǔ)

2019-07-05 08:56:50

預(yù)測性維護(hù)物聯(lián)網(wǎng)智能工廠
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 成人污污视频 | 97精品久久 | 天天插天天狠天天透 | 亚洲一区二区三区在线播放 | 国产在线精品一区二区三区 | 黄频免费 | 国产成人一区二 | 天天曰夜夜操 | 日本福利在线 | av黄色在线 | 天堂久久久久久久 | 亚洲精品片 | 成人免费精品视频 | 91在线精品秘密一区二区 | 国产精品99久久久久久久久久久久 | 美女三区 | av激情影院| av在线成人 | 午夜影晥| 久久一区二区视频 | 亚洲精品乱码久久久久久黑人 | 午夜在线视频一区二区三区 | 久久中文字幕一区 | 久久中文一区二区 | 99日韩| 欧美日韩视频在线 | 一区二区三区免费网站 | 99热欧美| 久久男人 | 日韩精品一区二区三区在线观看 | 日韩在线精品视频 | 成人在线精品 | 91在线视频免费观看 | 伊人无码高清 | 蜜臀网 | 成人黄色在线 | 国产精品99久久免费观看 | 日韩欧美大片在线观看 | 这里精品 | 日韩精品在线视频免费观看 | 一区二区三区高清在线观看 |