欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go Map并發(fā)沖突預(yù)防與解決

 更新時(shí)間:2022年12月15日 09:50:08   作者:小馬別過河  
這篇文章主要為大家介紹了Go Map并發(fā)沖突預(yù)防與解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

背景

關(guān)于 Go 語言的 Map,有兩個(gè)需要注意的特性:

  • Map 是并發(fā)讀寫不安全的,這是出于性能的考慮;
  • Map 并發(fā)讀寫導(dǎo)致的錯(cuò)誤,無法使用 recover 捕獲。

后者意味著,只有出現(xiàn)并發(fā)讀寫的問題,服務(wù)就會(huì)掛掉。

這兩個(gè)特性可能大家都知道,可即使有這個(gè)共識(shí),我還是見過這個(gè)問題導(dǎo)致的事故。

事故的大致情況是,一個(gè)人封裝了map的讀寫,沒有使用鎖。另一個(gè)人開協(xié)程讀寫 map。而測試環(huán)境請(qǐng)求量小,不一定會(huì)導(dǎo)致崩潰,于是,這個(gè)問題就留到生產(chǎn)環(huán)境才出現(xiàn)了。

除了靠開發(fā)者自覺和 code review,還能怎么預(yù)防這種情況呢?我覺得在單元測試加入并行測試也很重要。

并行單元測試

單元測試默認(rèn)不是并發(fā)的,比如下面的單測,是可以通過的:

func TestConcurrent(t *testing.T) {
    var m = map[string]int{}
    // 寫 map
    t.Run("write", func(t *testing.T) {
      for i := 0; i < 10000; i++ {
        m["a"] = 1
      }
    })
    // 讀 map
    t.Run("read", func(t *testing.T) {
      for i := 0; i < 10000; i++ {
        _ = m["a"]
      }
    })
}

但是我們的期望是,上面的單測不通過,該如何解決呢?

testing.T 有一個(gè) Parallel 方法,它表示當(dāng)前測試會(huì)和其他測試并行運(yùn)行。 如果參數(shù)有-test.count-test.cpu,一個(gè)測試可能運(yùn)行多次,同個(gè)測試的多個(gè)運(yùn)行實(shí)例,不會(huì)并行運(yùn)行。

我們給上面的單測,加上t.Parallel():

func TestConcurrent(t *testing.T) {
    var m = map[string]int{}
    t.Run("write", func(t *testing.T) {
      // 加上并行
      t.Parallel()
      for i := 0; i < 10000; i++ {
        m["a"] = 1
      }
    })
    t.Run("read", func(t *testing.T) {
      // 加上并行
      t.Parallel()
      for i := 0; i < 10000; i++ {
        _ = m["a"]
      }
    })
}

這次執(zhí)行就會(huì)報(bào)錯(cuò):

fatal error: concurrent map read and map write

支持并發(fā)的 Map

讓 Map 支持并發(fā)讀寫并不麻煩,常見的做法有:

  • 操作 map 的時(shí)候,加上讀寫鎖 sync.RWMutex;
  • 使用 sync.Map。

sync.RWMutex 大家用得可能比較多。這里簡單給個(gè)demo。

sync.RWMutex

我們給上面的單測加上鎖,這次運(yùn)行就能通過了。

func TestConcurrent(t *testing.T) {
	var m = map[string]int{}
	//  定義鎖,零值就可以使用
	var mu sync.RWMutex
	t.Run("write", func(t *testing.T) {
		t.Parallel()
		for i := 0; i < 10000; i++ {
			// 鎖
			mu.Lock()
			m["a"] = 1
			// 解鎖
			mu.Unlock()
		}
	})
	t.Run("read", func(t *testing.T) {
		t.Parallel()
		for i := 0; i < 10000; i++ {
			// 鎖
			mu.Lock()
			_ = m["a"]
			// 解鎖
			mu.Unlock()
		}
	})
}

本文的重點(diǎn)介紹一下Go標(biāo)準(zhǔn)庫自帶的,支持并發(fā)讀寫的 map:sync.Map

sync.Map

sync.Map 就是線程安全版的 map[interface{}]interface{},零值可以直接使用,值不能復(fù)制。它主要用于以下場景:

  • 當(dāng)同一個(gè) key 的值,寫少讀多的時(shí)候;
  • 但多個(gè) goroutines 讀寫或修改一系列不同的key的時(shí)候。

上面兩種場景中,比起帶Mutex(或RWMutex)的map,sync.Map 會(huì)大大減少鎖的競爭。

sync.Map 提供的方法不多,這里列出一些。注意的是,any 是 go 1.18 中 interface{}的別名。

Store,設(shè)置 key-value。

func (m *Map) Store(key, value any)

Load, 根據(jù) key 讀取 value。

func (m *Map) Load(key any) (value any, ok bool)

Delete,刪除某個(gè)key。

func (m *Map) Delete(key any)

Range,遍歷所有key, 如果f返回false,會(huì)停止遍歷。

func (m *Map) Range(f func(key, value any) bool)

還有 LoadAndDelete(讀后刪除)、LoadOrStore(讀key,不存在時(shí)設(shè)置)。

我們給上面的單測,使用sync.Map,測試也可以通過。

func TestConcurrent(t *testing.T) {
	// 可以使用零值
	var m sync.Map
	t.Run("write", func(t *testing.T) {
		t.Parallel()
		for i := 0; i < 10000; i++ {
			// 寫
			m.Store("a", 1)
		}
	})
	t.Run("read", func(t *testing.T) {
		t.Parallel()
		for i := 0; i < 10000; i++ {
			// 讀
			v, ok := m.Load("a")
			if ok {
				_ = v.(int)
			}
		}
	})
}

參考

pkg.go.dev/sync#Map

以上就是Go Map并發(fā)沖突預(yù)防與解決的詳細(xì)內(nèi)容,更多關(guān)于Go Map并發(fā)沖突的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • go build和go install的區(qū)別介紹

    go build和go install的區(qū)別介紹

    這篇文章主要介紹了go build和go install的區(qū)別介紹,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Golang Map類型的使用(增刪查改)

    Golang Map類型的使用(增刪查改)

    在Go中,map是哈希表的引用,是一種key-value數(shù)據(jù)結(jié)構(gòu),本文主要介紹了Golang Map類型的使用,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-05-05
  • Golang基于Vault實(shí)現(xiàn)敏感數(shù)據(jù)加解密

    Golang基于Vault實(shí)現(xiàn)敏感數(shù)據(jù)加解密

    數(shù)據(jù)加密是主要的數(shù)據(jù)安全防護(hù)技術(shù)之一,敏感數(shù)據(jù)應(yīng)該加密存儲(chǔ)在數(shù)據(jù)庫中,降低泄露風(fēng)險(xiǎn),本文將介紹一下利用Vault實(shí)現(xiàn)敏感數(shù)據(jù)加解密的方法,需要的可以參考一下
    2023-07-07
  • golang中值類型/指針類型的變量區(qū)別總結(jié)

    golang中值類型/指針類型的變量區(qū)別總結(jié)

    golang的值類型和指針類型receiver一直是大家比較混淆的地方,下面這篇文章主要給大家總結(jié)介紹了關(guān)于golang中值類型/指針類型的變量區(qū)別的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下。
    2017-12-12
  • 深入了解Golang中reflect反射基本原理

    深入了解Golang中reflect反射基本原理

    反射是這樣一種機(jī)制,它是可以讓我們?cè)诔绦蜻\(yùn)行時(shí)(runtime)訪問、檢測和修改對(duì)象本身狀態(tài)或行為的一種能力。本文主要帶大家來看看Golang中reflect反射基本原理,需要的可以參考一下
    2023-01-01
  • go?mod?tidy報(bào)錯(cuò):zip:?not?a?valid?zip?file解決辦法

    go?mod?tidy報(bào)錯(cuò):zip:?not?a?valid?zip?file解決辦法

    這篇文章主要給大家介紹了關(guān)于go?mod?tidy報(bào)錯(cuò):zip:?not?a?valid?zip?file的解決辦法,go mod是進(jìn)行代碼管理,這錯(cuò)誤是因?yàn)楸镜胤种Ш瓦h(yuǎn)程分支沖突,本文通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-01-01
  • go語言算法題解二叉樹的最小深度

    go語言算法題解二叉樹的最小深度

    這篇文章主要為大家介紹了go語言算法題解二叉樹的最小深度示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • golang-gorm自動(dòng)建表問題

    golang-gorm自動(dòng)建表問題

    這篇文章主要介紹了golang-gorm自動(dòng)建表問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • Go語言如何處理HTTP身份驗(yàn)證教程示例

    Go語言如何處理HTTP身份驗(yàn)證教程示例

    這篇文章主要為大家介紹了Go語言如何處理HTTP身份驗(yàn)證教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • 詳解Golang中字符串的使用

    詳解Golang中字符串的使用

    這篇文章主要為大家詳細(xì)介紹了Golang中字符串的使用,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Golang有一定的幫助,感興趣的小伙伴可以了解一下
    2022-10-10

最新評(píng)論