人妖在线一区,国产日韩欧美一区二区综合在线,国产啪精品视频网站免费,欧美内射深插日本少妇

新聞動(dòng)態(tài)

Go語言原子操作及互斥鎖的區(qū)別

發(fā)布日期:2022-02-04 08:39 | 文章來源:CSDN

原子操作就是不可中斷的操作,外界是看不到原子操作的中間狀態(tài),要么看到原子操作已經(jīng)完成,要么看到原子操作已經(jīng)結(jié)束。在某個(gè)值的原子操作執(zhí)行的過程中,CPU絕對(duì)不會(huì)再去執(zhí)行其他針對(duì)該值的操作,那么其他操作也是原子操作。

Go語言中提供的原子操作都是非侵入式的,在標(biāo)準(zhǔn)庫代碼包sync/atomic中提供了相關(guān)的原子函數(shù)。

增或減

用于增或減的原子操作的函數(shù)名稱都是以"Add"開頭的,后面跟具體的類型名,比如下面這個(gè)示例就是int64類型的原子減操作

func main() {
var  counter int64 =  23
atomic.AddInt64(&counter,-3)
fmt.Println(counter)
}
---output---
20

原子函數(shù)的第一個(gè)參數(shù)都是指向變量類型的指針,是因?yàn)樵硬僮餍枰涝撟兞吭趦?nèi)存中的存放位置,然后加以特殊的CPU指令,也就是說對(duì)于不能取得內(nèi)存存放地址的變量是無法進(jìn)行原子操作的。第二個(gè)參數(shù)的類型會(huì)自動(dòng)轉(zhuǎn)換為與第一個(gè)參數(shù)相同的類型。此外,原子操作會(huì)自動(dòng)將操作后的值賦值給變量,無需我們自己手動(dòng)賦值了。

對(duì)于 atomic.AddUint32() 和 atomic.AddUint64() 的第二個(gè)參數(shù)為 uint32 與 uint64,因此無法直接傳遞一個(gè)負(fù)的數(shù)值進(jìn)行減法操作,Go語言提供了另一種方法來迂回實(shí)現(xiàn):使用二進(jìn)制補(bǔ)碼的特性

注意:unsafe.Pointer 類型的值無法被加減。

比較并交換(Compare And Swap)

簡(jiǎn)稱CAS,在標(biāo)準(zhǔn)庫代碼包sync/atomic中以”Compare And Swap“為前綴的若干函數(shù)就是CAS操作函數(shù),比如下面這個(gè)

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

第一個(gè)參數(shù)的值是這個(gè)變量的指針,第二個(gè)參數(shù)是這個(gè)變量的舊值,第三個(gè)參數(shù)指的是這個(gè)變量的新值。

運(yùn)行過程:調(diào)用CompareAndSwapInt32 后,會(huì)先判斷這個(gè)指針上的值是否跟舊值相等,若相等,就用新值覆蓋掉這個(gè)值,若相等,那么后面的操作就會(huì)被忽略掉。返回一個(gè) swapped 布爾值,表示是否已經(jīng)進(jìn)行了值替換操作。

與鎖有不同之處:鎖總是假設(shè)會(huì)有并發(fā)操作修改被操作的值,而CAS總是假設(shè)值沒有被修改,因此CAS比起鎖要更低的性能損耗,鎖被稱為悲觀鎖,而CAS被稱為樂觀鎖。

CAS的使用示例

var value int32
func AddValue(delta int32)  {
for {
v:= value
if atomic.CompareAndSwapInt32(&value,v,(v+delta)) {
break
}
}
}

由示例可以看出,我們需要多次使用for循環(huán)來判斷該值是否已被更改,為了保證CAS操作成功,僅在 CompareAndSwapInt32 返回為 true時(shí)才退出循環(huán),這跟自旋鎖的自旋行為相似。

載入與存儲(chǔ)

對(duì)一個(gè)值進(jìn)行讀或?qū)憰r(shí),并不代表這個(gè)值是最新的值,也有可能是在在讀或?qū)懙倪^程中進(jìn)行了并發(fā)的寫操作導(dǎo)致原值改變。為了解決這問題,Go語言的標(biāo)準(zhǔn)庫代碼包sync/atomic提供了原子的讀取(Load為前綴的函數(shù))或?qū)懭耄⊿tore為前綴的函數(shù))某個(gè)值

將上面的示例改為原子讀取

var value int32
func AddValue(delta int32)  {
for {
v:= atomic.LoadInt32(&value)
if atomic.CompareAndSwapInt32(&value,v,(v+delta)) {
break
}
}
}

原子寫入總會(huì)成功,因?yàn)樗恍枰P(guān)心原值是什么,而CAS中必須關(guān)注舊值,因此原子寫入并不能代替CAS,原子寫入包含兩個(gè)參數(shù),以下面的StroeInt32為例:

//第一個(gè)參數(shù)是被操作值的指針,第二個(gè)是被操作值的新值
func StoreInt32(addr *int32, val int32) 

交換

這類操作都以”Swap“開頭的函數(shù),稱為”原子交換操作“,功能與之前說的CAS操作與原子寫入操作有相似之處。

func SwapInt32(addr *int32, new int32) (old int32)

以 SwapInt32 為例,第一個(gè)參數(shù)是int32類型的指針,第二個(gè)是新值。原子交換操作不需要關(guān)心原值,而是直接設(shè)置新值,但是會(huì)返回被操作值的舊值。

原子值

Go語言的標(biāo)準(zhǔn)庫代碼包sync/atomic中有一個(gè)叫做Value的原子值,它是一個(gè)結(jié)構(gòu)體類型,用于存儲(chǔ)需要原子讀寫的值,結(jié)構(gòu)體如下

// Value提供原子加載并存儲(chǔ)一致類型的值。
// Value的零值從Load返回nil。
//調(diào)用Store后,不得復(fù)制值。
//首次使用后不得復(fù)制值。
type Value struct {
v interface{}
}

可以看出結(jié)構(gòu)體內(nèi)是一個(gè) v interface{},也就是說 該Value原子值可以保存任何類型的需要原子讀寫的值。

使用方式如下:

var Atomicvalue  atomic.Value

該類型有兩個(gè)公開的指針方法

//原子的讀取原子值實(shí)例中存儲(chǔ)的值,返回一個(gè) interface{} 類型的值,且不接受任何參數(shù)。
//若未曾通過store方法存儲(chǔ)值之前,會(huì)返回nil
func (v *Value) Load() (x interface{})
//原子的在原子實(shí)例中存儲(chǔ)一個(gè)值,接收一個(gè) interface{} 類型(不能為nil)的參數(shù),且不會(huì)返回任何值
func (v *Value) Store(x interface{})

一旦原子值實(shí)例存儲(chǔ)了某個(gè)類型的值,那么之后Store存儲(chǔ)的值就必須是與該類型一致,否則就會(huì)引發(fā)panic。

嚴(yán)格來講,atomic.Value類型的變量一旦被聲明,就不應(yīng)該被復(fù)制到其他地方。比如:作為源值賦值給其他變量,作為參數(shù)傳遞給函數(shù),作為結(jié)果值從函數(shù)返回,作為元素值通過通道傳遞,這些都會(huì)造成值的復(fù)制。

但是atomic.Value類型的指針類型變量就不會(huì)存在這個(gè)問題,原因是對(duì)結(jié)構(gòu)體的復(fù)制不但會(huì)生成該值的副本,還會(huì)生成其中字段的副本,這樣那么并發(fā)引發(fā)的值變化都與原值沒關(guān)系了。

看下面這個(gè)小示例

func main() {
var Atomicvalue  atomic.Value
Atomicvalue.Store([]int{1,2,3,4,5})
anotherStore(Atomicvalue)
fmt.Println("main: ",Atomicvalue)
}
func anotherStore(Atomicvalue atomic.Value)  {
Atomicvalue.Store([]int{6,7,8,9,10})
fmt.Println("anotherStore: ",Atomicvalue)
}
---output---
anotherStore:  {[6 7 8 9 10]}
main:  {[1 2 3 4 5]}

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

互斥鎖是一種數(shù)據(jù)結(jié)構(gòu),使你可以執(zhí)行一系列互斥操作。而原子操作是互斥的單個(gè)操作,這意味著沒有其他線程可以打斷它。那么就Go語言里atomic包里的原子操作和sync包提供的同步鎖有什么不同呢?

首先atomic操作的優(yōu)勢(shì)是更輕量,比如CAS可以在不形成臨界區(qū)和創(chuàng)建互斥量的情況下完成并發(fā)安全的值替換操作。這可以大大的減少同步對(duì)程序性能的損耗。

原子操作也有劣勢(shì)。還是以CAS操作為例,使用CAS操作的做法趨于樂觀,總是假設(shè)被操作值未曾被改變(即與舊值相等),并一旦確認(rèn)這個(gè)假設(shè)的真實(shí)性就立即進(jìn)行值替換,那么在被操作值被頻繁變更的情況下,CAS操作并不那么容易成功。而使用互斥鎖的做法則趨于悲觀,我們總假設(shè)會(huì)有并發(fā)的操作要修改被操作的值,并使用鎖將相關(guān)操作放入臨界區(qū)中加以保護(hù)。

所以總結(jié)下來原子操作與互斥鎖的區(qū)別有:

互斥鎖是一種數(shù)據(jù)結(jié)構(gòu),用來讓一個(gè)線程執(zhí)行程序的關(guān)鍵部分,完成互斥的多個(gè)操作。
原子操作是針對(duì)某個(gè)值的單個(gè)互斥操作。
可以把互斥鎖理解為悲觀鎖,共享資源每次只給一個(gè)線程使用,其它線程阻塞,用完后再把資源轉(zhuǎn)讓給其它線程。
atomic包提供了底層的原子性內(nèi)存原語,這對(duì)于同步算法的實(shí)現(xiàn)很有用。這些函數(shù)一定要非常小心地使用,使用不當(dāng)反而會(huì)增加系統(tǒng)資源的開銷,對(duì)于應(yīng)用層來說,最好使用通道或sync包中提供的功能來完成同步操作。

針對(duì)atomic包的觀點(diǎn)在Google的郵件組里也有很多討論,其中一個(gè)結(jié)論解釋是:

應(yīng)避免使用該包裝。或者,閱讀C ++ 11標(biāo)準(zhǔn)的“原子操作”一章;如果您了解如何在C ++中安全地使用這些操作,那么你才能有安全地使用Go的 sync/atomic包的能力。

到此這篇關(guān)于Go語言原子操作及互斥鎖的區(qū)別 的文章就介紹到這了,更多相關(guān)Go語言原子操作內(nèi)容請(qǐng)搜索本站以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持本站!

香港服務(wù)器租用

版權(quán)聲明:本站文章來源標(biāo)注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請(qǐng)保持原文完整并注明來源及原文鏈接。禁止復(fù)制或仿造本網(wǎng)站,禁止在非www.sddonglingsh.com所屬的服務(wù)器上建立鏡像,否則將依法追究法律責(zé)任。本站部分內(nèi)容來源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來,僅供學(xué)習(xí)參考,不代表本站立場(chǎng),如有內(nèi)容涉嫌侵權(quán),請(qǐng)聯(lián)系alex-e#qq.com處理。

相關(guān)文章

實(shí)時(shí)開通

自選配置、實(shí)時(shí)開通

免備案

全球線路精選!

全天候客戶服務(wù)

7x24全年不間斷在線

專屬顧問服務(wù)

1對(duì)1客戶咨詢顧問

在線
客服

在線客服:7*24小時(shí)在線

客服
熱線

400-630-3752
7*24小時(shí)客服服務(wù)熱線

關(guān)注
微信

關(guān)注官方微信
頂部