日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

go 原子操作 atomic

發布時間:2024/2/28 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 go 原子操作 atomic 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. 什么是原子操作

  我們已經知道,原子操作即是進行過程中不能被中斷的操作。也就是說,針對某個值的原子操作在被進行的過程當中,CPU絕不會再去進行其它的針對該值的操作。無論這些其它的操作是否為原子操作都會是這樣。為了實現這樣的嚴謹性,原子操作僅會由一個獨立的CPU指令代表和完成。只有這樣才能夠在并發環境下保證原子操作的絕對安全。
Go語言提供的原子操作都是非侵入式的。它們由標準庫代碼包sync/atomic中的眾多函數代表。我們可以通過調用這些函數對幾種簡單的類型的值進行原子操作。

2.goalng 中的原子操作類型

  int32、int64、uint32、uint64、uintptr和unsafe.Pointer類型,共6個

3.golang 中有哪些原子操作

  有5種,即:增或減、比較并交換、載入、存儲、交換。

4.詳解

  ?1. 增或減
被用于進行增或減的原子操作(以下簡稱原子增/減操作)的函數名稱都以“Add”為前綴,并后跟針對的具體類型的名稱。例如,實現針對uint32類型的原子增/減操作的函數的名稱為AddUint32。事實上,sync/atomic包中的所有函數的命名都遵循此規則。

  2. 比較并交換
有些讀者可能很熟悉比較并交換操作的英文稱謂——Compare And Swap,簡稱CAS。在sync/atomic包中,這類原子操作由名稱以“CompareAndSwap”為前綴的若干個函數代表。
我們依然以針對int32類型值的函數為例。該函數名為CompareAndSwapInt32。其聲明如下:

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

可以看到,CompareAndSwapInt32函數接受三個參數。第一個參數的值應該是指向被操作值的指針值。該值的類型即為*int32。后兩個參數的類型都是int32類型。它們的值應該分別代表被操作值的舊值和新值。CompareAndSwapInt32函數在被調用之后會先判斷參數addr指向的被操作值與參數old的值是否相等。僅當此判斷得到肯定的結果之后,該函數才會用參數new代表的新值替換掉原先的舊值。否則,后面的替換操作就會被忽略。這正是“比較并交換”這個短語的由來。CompareAndSwapInt32函數的結果swapped被用來表示是否進行了值的替換操作。
與我們前面講到的鎖相比,CAS操作有明顯的不同。它總是假設被操作值未曾被改變(即與舊值相等),并一旦確認這個假設的真實性就立即進行值替換。而使用鎖則是更加謹慎的做法。我們總是先假設會有并發的操作要修改被操作值,并使用鎖將相關操作放入臨界區中加以保護。我們可以說,使用鎖的做法趨于悲觀,而CAS操作的做法則更加樂觀。
CAS操作的優勢是,可以在不形成臨界區和創建互斥量的情況下完成并發安全的值替換操作。這可以大大的減少同步對程序性能的損耗。當然,CAS操作也有劣勢。在被操作值被頻繁變更的情況下,CAS操作并不那么容易成功。有些時候,我們可能不得不利用for循環以進行多次嘗試。示例如下:

1

2

3

4

5

6

7

8

9

var?value int32

func?addValue(delta int32) {

  for?{

    v := value

    if?atomic.CompareAndSwapInt32(&value, v, (v + delta)) {

      break

    }

  }

}

  可以看到,為了保證CAS操作的成功完成,我們僅在CompareAndSwapInt32函數的結果值為true時才會退出循環。這種做法與自旋鎖的自旋行為相似。addValue函數會不斷的嘗試原子的更新value的值,直到這一操作成功為止。操作失敗的緣由總會是value的舊值已不與v的值相等了。如果value的值會被并發的修改的話,那么發生這種情況是很正常的。
CAS操作雖然不會讓某個Goroutine阻塞在某條語句上,但是仍可能會使流程的執行暫時停滯。不過,這種停滯的時間大都極其短暫。
請記住,當想并發安全的更新一些類型(更具體的講是,前文所述的那6個類型)的值的時候,我們總是應該優先選擇CAS操作。
與此對應,被用來進行原子的CAS操作的函數共有6個。除了我們已經講過的CompareAndSwapInt32函數之外,還有CompareAndSwapInt64、CompareAndSwapPointer、CompareAndSwapUint32、CompareAndSwapUint64 和CompareAndSwapUintptr函數。這些函數的結果聲明列表與CompareAndSwapInt32函數的完全一致。而它們的參數聲明列表與后者也非常類似。雖然其中的那三個參數的類型不同,但其遵循的規則是一致的,即:第二個和第三個參數的類型均為與第一個參數的類型(即某個指針類型)緊密相關的那個類型。例如,如果第一個參數的類型為*unsafe.Pointer,那么后兩個參數的類型就一定是unsafe.Pointer。這也是由這三個參數的含義決定的。

  

3. 載入
在前面示例的for循環中,我們使用語句v := value為變量v賦值。但是,要注意,其中的讀取value的值的操作并不是并發安全的。在該讀取操作被進行的過程中,其它的對此值的讀寫操作是可以被同時進行的。它們并不會受到任何限制。
在第7章的第1節的最后,我們舉過這樣一個例子:在32位計算架構的計算機上寫入一個64位的整數。如果在這個寫操作未完成的時候有一個讀操作被并發的進行了,那么這個讀操作很可能會讀取到一個只被修改了一半的數據。這種結果是相當糟糕的。
為了原子的讀取某個值,sync/atomic代碼包同樣為我們提供了一系列的函數。這些函數的名稱都以“Load”為前綴,意為載入。我們依然以針對int32類型值的那個函數為例。
我們下面利用LoadInt32函數對上一個示例稍作修改:制。

1

2

3

4

5

6

7

8

9

func?addValue(delta int32) {

????for?{

????????v := atomic.LoadInt32(&value)

????????if?atomic.CompareAndSwapInt32(&value, v, (v + delta)) {

????????????break

????????}

????}

}

????????????????

  函數atomic.LoadInt32接受一個*int32類型的指針值,并會返回該指針值指向的那個值。在該示例中,我們使用調用表達式atomic.LoadInt32(&value)替換掉了標識符value。替換后,那條賦值語句的含義就變為:原子的讀取變量value的值并把它賦給變量v。有了“原子的”這個形容詞就意味著,在這里讀取value的值的同時,當前計算機中的任何CPU都不會進行其它的針對此值的讀或寫操作。這樣的約束是受到底層硬件的支持的。
注意,雖然我們在這里使用atomic.LoadInt32函數原子的載入value的值,但是其后面的CAS操作仍然是有必要的。因為,那條賦值語句和if語句并不會被原子的執行。在它們被執行期間,CPU仍然可能進行其它的針對value的值的讀或寫操作。也就是說,value的值仍然有可能被并發的改變。
與atomic.LoadInt32函數的功能類似的函數有atomic.LoadInt64、atomic.LoadPointer、atomic.LoadUint32、atomic.LoadUint64和atomic.LoadUintptr。

  4. 存儲
與讀取操作相對應的是寫入操作。而sync/atomic包也提供了與原子的值載入函數相對應的原子的值存儲函數。這些函數的名稱均以“Store”為前綴。

?

  5. 交換
在sync/atomic代碼包中還存在著一類函數。它們的功能與前文所講的CAS操作和原子載入操作都有些類似。這樣的功能可以被稱為原子交換操作。這類函數的名稱都以“Swap”為前綴。

總結

以上是生活随笔為你收集整理的go 原子操作 atomic的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。