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

Go語言atomic.Value如何不加鎖保證數(shù)據(jù)線程安全?

 更新時(shí)間:2023年05月12日 09:47:00   作者:心如花木向陽生  
這篇文章主要介紹了Go語言atomic.Value如何不加鎖保證數(shù)據(jù)線程安全詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

很多人可能沒有注意過,在 Go(甚至是大部分語言)中,一條普通的賦值語句其實(shí)不是一個(gè)原子操作。例如,在32位機(jī)器上寫int64類型的變量就會(huì)有中間狀態(tài),它會(huì)被拆成兩次寫操作(匯編的MOV指令)——寫低 32 位和寫高 32 位。32機(jī)器上對(duì)int64進(jìn)行賦值

如果一個(gè)線程剛寫完低32位,還沒來得及寫高32位時(shí),另一個(gè)線程讀取了這個(gè)變量,那它得到的就是一個(gè)毫無邏輯的中間變量,這很有可能使我們的程序出現(xiàn)Bug。

這還只是一個(gè)基礎(chǔ)類型,如果我們對(duì)一個(gè)結(jié)構(gòu)體進(jìn)行賦值,那它出現(xiàn)并發(fā)問題的概率就更高了。很可能寫線程剛寫完一小半的字段,讀線程就來讀取這個(gè)變量,那么就只能讀到僅修改了一部分的值。這顯然破壞了變量的完整性,讀出來的值也是完全錯(cuò)誤的。

面對(duì)這種多線程下變量的讀寫問題,Go給出的解決方案是atomic.Value,它使得我們可以不依賴于不保證兼容性的unsafe.Pointer類型,同時(shí)又能將任意數(shù)據(jù)類型的讀寫操作封裝成原子性操作。

atomic.Value的使用方式

atomic.Value類型對(duì)外提供了兩個(gè)讀寫方法:

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

下面是一個(gè)簡單的例子演示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 個(gè)協(xié)程并發(fā)更新
	for i := 0; i < 10; i++ {
		go func(i int) {
			defer wg.Done()
			update(i, i+5)
		}(i)
	}
	wg.Wait()
	r := rect.Load().(*Rectangle)
	fmt.Printf("rect.width=%d\nrect.length=%d\n", r.width, r.length)
}

你可能會(huì)好奇,為什么atomic.Value在不加鎖的情況下就提供了讀寫變量的線程安全保證,接下來我們就一起看看其內(nèi)部實(shí)現(xiàn)。

atomic.Value的內(nèi)部實(shí)現(xiàn)

atomic.Value被設(shè)計(jì)用來存儲(chǔ)任意類型的數(shù)據(jù),所以它內(nèi)部的字段是一個(gè)interface{}類型。

// A Value provides an atomic load and store of a consistently typed value.
// The zero value for a Value returns nil from Load.
// Once Store has been called, a Value must not be copied.
//
// A Value must not be copied after first use.
type Value struct {
	v interface{}
}

除了Value外,atomic包內(nèi)部定義了一個(gè)ifaceWords類型,這其實(shí)是interface{}的內(nèi)部表示 (runtime.eface),它的作用是將interface{}類型分解,得到其原始類型(typ)和真正的值(data)。

// ifaceWords is interface{} internal representation.
type ifaceWords struct {
	typ  unsafe.Pointer
	data unsafe.Pointer
}

寫入線程安全的保證

直接來看代碼

// Store sets the value of the Value to x.
// All calls to Store for a given Value must use values of the same concrete type.
// Store of an inconsistent type panics, as does Store(nil).
func (v *Value) Store(val interface{}) {
	if val == nil {
		panic("sync/atomic: store of nil value into Value")
	}
    // 通過unsafe.Pointer將現(xiàn)有的(v)和要寫入的值(val) 分別轉(zhuǎn)成ifaceWords類型。
    // 這樣我們下一步就可以得到這兩個(gè)interface{}的原始類型(typ)和真正的值(data)。
	vp := (*ifaceWords)(unsafe.Pointer(v))
	vlp := (*ifaceWords)(unsafe.Pointer(&val))
	for {
		typ := LoadPointer(&vp.typ)
		if typ == nil {
			// Attempt to start first store.
			// Disable preemption so that other goroutines can use
			// active spin wait to wait for completion; and so that
			// GC does not see the fake type accidentally.
			runtime_procPin()
			if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(^uintptr(0))) {
				runtime_procUnpin()
				continue
			}
			// Complete first store.
			StorePointer(&vp.data, vlp.data)
			StorePointer(&vp.typ, vlp.typ)
			runtime_procUnpin()
			return
		}
		if uintptr(typ) == ^uintptr(0) {
			// First store in progress. Wait.
			// Since we disable preemption around the first store,
			// we can wait with active spinning.
			continue
		}
		// First store completed. Check type and overwrite data.
		if typ != vlp.typ {
			panic("sync/atomic: store of inconsistently typed value into Value")
		}
		StorePointer(&vp.data, vlp.data)
		return
	}
}

大概的邏輯:

  • 開始就是一個(gè)無限 for 循環(huán)。配合CompareAndSwap使用,可以達(dá)到樂觀鎖的效果。
  • 通過LoadPointer這個(gè)原子操作拿到當(dāng)前Value中存儲(chǔ)的類型。下面根據(jù)這個(gè)類型的不同,分3種情況處理。
  • 第一次寫入

    一個(gè)atomic.Value實(shí)例被初始化后,它的typdata字段會(huì)被設(shè)置為指針的零值 nil,所以先判斷如果typ是否為nil,如果是那就證明這個(gè)Value實(shí)例還未被寫入過數(shù)據(jù)。那之后就是一段初始寫入的操作:

  • runtime_procPin()這是runtime中的一段函數(shù),一方面它禁止了調(diào)度器對(duì)當(dāng)前 goroutine 的搶占(preemption),使得它在執(zhí)行當(dāng)前邏輯的時(shí)候不被其他goroutine打斷,以便可以盡快地完成工作。另一方面,在禁止搶占期間,GC 線程也無法被啟用,這樣可以防止 GC 線程看到一個(gè)莫名其妙的指向^uintptr(0)的類型(這是賦值過程中的中間狀態(tài))。

    1)使用CAS操作,先嘗試將typ設(shè)置為^uintptr(0)這個(gè)中間狀態(tài)。如果失敗,則證明已經(jīng)有別的線程搶先完成了賦值操作,那它就解除搶占鎖,然后重新回到 for 循環(huán)第一步。

    2)如果設(shè)置成功,那證明當(dāng)前線程搶到了這個(gè)"樂觀鎖”,它可以安全的把v設(shè)為傳入的新值了。注意,這里是先寫data字段,然后再寫typ字段。因?yàn)槲覀兪且?code>typ字段的值作為寫入完成與否的判斷依據(jù)的

  • 第一次寫入還未完成

    如果看到typ字段還是^uintptr(0)這個(gè)中間類型,證明剛剛的第一次寫入還沒有完成,所以它會(huì)繼續(xù)循環(huán),一直等到第一次寫入完成。

  • 第一次寫入已完成

    首先檢查上一次寫入的類型與這一次要寫入的類型是否一致,如果不一致則拋出異常。反之,則直接把這一次要寫入的值寫入到data字段。

這個(gè)邏輯的主要思想就是,為了完成多個(gè)字段的原子性寫入,我們可以抓住其中的一個(gè)字段,以它的狀態(tài)來標(biāo)志整個(gè)原子寫入的狀態(tài)。

讀?。↙oad)操作

先上代碼:

// Load returns the value set by the most recent Store.
// It returns nil if there has been no call to Store for this Value.
func (v *Value) Load() (val interface{}) {
	vp := (*ifaceWords)(unsafe.Pointer(v))
	typ := LoadPointer(&vp.typ)
	if typ == nil || uintptr(typ) == ^uintptr(0) {
		// First store not yet completed.
		return nil
	}
	data := LoadPointer(&vp.data)
	vlp := (*ifaceWords)(unsafe.Pointer(&val))
	vlp.typ = typ
	vlp.data = data
	return
}

讀取相對(duì)就簡單很多了,它有兩個(gè)分支:

  • 如果當(dāng)前的typ是 nil 或者^uintptr(0),那就證明第一次寫入還沒有開始,或者還沒完成,那就直接返回 nil (不對(duì)外暴露中間狀態(tài))。
  • 否則,根據(jù)當(dāng)前看到的typdata構(gòu)造出一個(gè)新的interface{}返回出去。

總結(jié)

本文由淺入深的介紹了atomic.Value的使用姿勢,以及內(nèi)部實(shí)現(xiàn)。另外,原子操作由底層硬件支持,對(duì)于一個(gè)變量更新的保護(hù),原子操作通常會(huì)更有效率,并且更能利用計(jì)算機(jī)多核的優(yōu)勢,如果要更新的是一個(gè)復(fù)合對(duì)象,則應(yīng)當(dāng)使用atomic.Value封裝好的實(shí)現(xiàn)。

而我們做并發(fā)同步控制常用到的Mutex鎖,則是由操作系統(tǒng)的調(diào)度器實(shí)現(xiàn),鎖應(yīng)當(dāng)用來保護(hù)一段邏輯。

以上就是Go語言atomic.Value如何不加鎖保證數(shù)據(jù)線程安全?的詳細(xì)內(nèi)容,更多關(guān)于Go語言atomic.Value如何不加鎖保證數(shù)據(jù)線程安全?的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

您可能感興趣的文章:

相關(guān)文章

  • Golang?strings包常用字符串操作函數(shù)

    Golang?strings包常用字符串操作函數(shù)

    Golang?中的字符串統(tǒng)一使用?UTF-8?(屬于Unicode編碼的一種實(shí)現(xiàn)方式)進(jìn)行編碼,本篇文章將結(jié)合具體實(shí)例對(duì)常用的字符串操作函數(shù)進(jìn)行介紹,感興趣的可以了解一下
    2021-12-12
  • Go語言中的range用法實(shí)例分析

    Go語言中的range用法實(shí)例分析

    這篇文章主要介紹了Go語言中的range用法,實(shí)例分析了range的功能與使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • golang中拿slice當(dāng)queue和拿list當(dāng)queue使用分析

    golang中拿slice當(dāng)queue和拿list當(dāng)queue使用分析

    這篇文章主要為大家介紹了golang?中拿slice當(dāng)queue和拿list當(dāng)queue使用分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • Go語言中日志的規(guī)范使用建議分享

    Go語言中日志的規(guī)范使用建議分享

    在任何服務(wù)端的語言項(xiàng)目中,日志是至關(guān)重要的組成部分,本文為大家整理了一些如何規(guī)范使用GO語言日志的建議,以及相應(yīng)的實(shí)際示例,希望對(duì)大家有事幫助
    2024-01-01
  • golang 實(shí)現(xiàn)一個(gè)restful微服務(wù)的操作

    golang 實(shí)現(xiàn)一個(gè)restful微服務(wù)的操作

    這篇文章主要介紹了golang 實(shí)現(xiàn)一個(gè)restful微服務(wù)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • 基于Go語言實(shí)現(xiàn)應(yīng)用IP防火墻

    基于Go語言實(shí)現(xiàn)應(yīng)用IP防火墻

    在公司里面經(jīng)常會(huì)聽到某應(yīng)用有安全漏洞問題,沒有做安全加固,IP防火墻就是一個(gè)典型的安全加固解決方案,下面我們就來學(xué)習(xí)一下如何使用go語言實(shí)現(xiàn)IP防火墻吧
    2023-11-11
  • Go處理包含多種引號(hào)的字符串的幾種方法

    Go處理包含多種引號(hào)的字符串的幾種方法

    在Go中,有幾種方式可以處理包含多種引號(hào)的字符串,以確保代碼的可讀性和正確性,本文將給大家詳細(xì)介紹了這幾種處理方式,并通過代碼示例講解的非常詳細(xì),需要的朋友可以參考下
    2024-04-04
  • Go語言題解LeetCode35搜索插入位置示例詳解

    Go語言題解LeetCode35搜索插入位置示例詳解

    這篇文章主要為大家介紹了Go語言題解LeetCode35搜索插入位置示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • Go語言簡介和環(huán)境配置

    Go語言簡介和環(huán)境配置

    Go語言保證了既能到達(dá)靜態(tài)編譯語言的安全和性能,又達(dá)到了動(dòng)態(tài)語言開發(fā)速度和易維護(hù)性,有人形容Go語言:Go=?C?+?Python?,?說明Go語言既有C靜態(tài)語言程序的運(yùn)行速度,又能達(dá)到Python動(dòng)態(tài)語言的快速開發(fā),這篇文章主要介紹了Go介紹和環(huán)境配置,需要的朋友可以參考下
    2022-07-07
  • 一文帶你掌握Go語言并發(fā)模式中的Context的上下文管理

    一文帶你掌握Go語言并發(fā)模式中的Context的上下文管理

    在?Go?的日常開發(fā)中,Context?上下文對(duì)象無處不在,無論是處理網(wǎng)絡(luò)請(qǐng)求、數(shù)據(jù)庫操作還是調(diào)用?RPC?等場景,那你真的熟悉它的正確用法嗎,隨著本文一探究竟吧
    2023-05-05

最新評(píng)論

乌拉特前旗| 肥城市| 焦作市| 基隆市| 隆回县| 兴业县| 上蔡县| 沅江市| 大足县| 壶关县| 武川县| 吉安市| 徐汇区| 永吉县| 大宁县| 印江| 互助| 翁牛特旗| 寻乌县| 郑州市| 胶州市| 淳安县| 齐齐哈尔市| 铜鼓县| 永丰县| 礼泉县| 绥阳县| 易门县| 鹤峰县| 洞头县| 黄冈市| 呼伦贝尔市| 太康县| 精河县| 达州市| 广德县| 鄯善县| 洛宁县| 吴堡县| 义马市| 湟源县|