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

如何避免go的map競(jìng)態(tài)問(wèn)題的方法

 更新時(shí)間:2023年02月09日 09:21:03   作者:有想法的工程師  
本文主要介紹了如何避免go的map競(jìng)態(tài)問(wèn)題的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

背景

在使用go語(yǔ)言開(kāi)發(fā)的過(guò)程中,我碰到過(guò)這樣一種情況,就是代碼自測(cè)沒(méi)問(wèn)題,代碼檢查沒(méi)問(wèn)題,上線跑了一段時(shí)間時(shí)間了也沒(méi)問(wèn)題,就是突然偶爾會(huì)抽風(fēng)panic,導(dǎo)致程序所在的pod(k8s的運(yùn)行docker鏡像的最小單位)重啟了,而程序里拋出來(lái)的異常如下

,意思是多個(gè)協(xié)程正在同時(shí)對(duì)同一個(gè)map變量進(jìn)行讀寫(xiě),這個(gè)就涉及到go程序的競(jìng)態(tài)問(wèn)題,而競(jìng)態(tài)問(wèn)題也是我們?nèi)粘i_(kāi)發(fā)中遇到比較多的情況

為什么會(huì)出現(xiàn)競(jìng)態(tài)問(wèn)題

出現(xiàn)這個(gè)問(wèn)題的主要原因是有多個(gè)協(xié)程在對(duì)同一個(gè)map變量進(jìn)行修改,這樣就可能會(huì)出現(xiàn)map被一個(gè)協(xié)程修改到一半的時(shí)候,然后另外一個(gè)協(xié)程就來(lái)讀取了,導(dǎo)致讀到一個(gè)“半成品”的map變量。而這個(gè)就說(shuō)明一個(gè)問(wèn)題,就是map類型并不是并發(fā)安全的

而并發(fā)安全的定義就是:在高并發(fā)下,進(jìn)程、線程(協(xié)程)出現(xiàn)資源競(jìng)爭(zhēng),導(dǎo)致出現(xiàn)臟讀,臟寫(xiě),死鎖等情況。

那么go語(yǔ)言有如下幾種類型不具備并發(fā)安全:map,slice,struct,channel,string

不過(guò)奇怪的是,只有map類型發(fā)生并發(fā)競(jìng)爭(zhēng)的時(shí)候,才會(huì)拋出fatal error,這個(gè)是無(wú)法被recover的,一定會(huì)中斷程序,而這也導(dǎo)致程序運(yùn)行的pod會(huì)被檢測(cè)出異常從而重啟

查了資料,有一種說(shuō)法是,map大部分會(huì)被用來(lái)存配置文件,而配置文件出錯(cuò)可能會(huì)導(dǎo)致一些嚴(yán)重的業(yè)務(wù)問(wèn)題,所以寧愿程序崩潰也要保全業(yè)務(wù)數(shù)據(jù)不會(huì)出現(xiàn)臟數(shù)據(jù)(只是一種說(shuō)法,不用太過(guò)在意)

如何解決競(jìng)態(tài)問(wèn)題

1、使用go的一些并發(fā)原語(yǔ)

如果需要修改的變量是程序啟動(dòng)之后就不需要修改的配置,那么可以使用sync.Once包來(lái)處理,這個(gè)包的作用就是限制一件事情只做一次,示例代碼如下

type User struct {
   Name string
   Other map[string]interface{}
   ConfigOnce sync.Once
}
 
// InitConfigOnce
// @description "初始化配置信息,只執(zhí)行一次"
// @auth yezibin 2023-01-21 15:38:09
// @param name string "description"
// @param other map[string]interface{} "description"
// @return *User "description"
func (u *User)InitConfigOnce(name string, other map[string]interface{}) *User {
   //Do包起來(lái)的方法,只會(huì)執(zhí)行一次,但是必須是同一個(gè)sync.Once變量
   u.ConfigOnce.Do(func() {
      fmt.Println("ok")
      u.Name = name
      u.Other = other
   })
   return u
}
 
// GetUserConfig
// @description "打印配置文件"
// @auth yezibin 2023-01-21 15:38:36
func (u *User) GetUserConfig()  {
   fmt.Println(u)
}

2、加讀寫(xiě)鎖(RWMutex map)

出現(xiàn)競(jìng)態(tài)的本質(zhì)是因?yàn)槎鄠€(gè)協(xié)程對(duì)同一個(gè)變量同時(shí)進(jìn)行讀與寫(xiě),通過(guò)用鎖來(lái)防止這個(gè)情況,因?yàn)槲遗e得案例是讀多寫(xiě)少的情況,用上讀寫(xiě)鎖性能會(huì)更好,示例代碼如下

type Mmap struct {
   Data map[string]interface{}
   Mu sync.RWMutex //因?yàn)橹饕桥渲茫瑢儆谧x多寫(xiě)少情況,所以使用讀寫(xiě)鎖提高鎖的性能
}
 
 
// InitMmap
// @description "初始化讀寫(xiě)鎖的map結(jié)構(gòu)體"
// @auth yezibin 2023-01-21 00:09:30
// @return *Config "description"
func InitMmap() *Mmap {
   return &Mmap{
      Data: make(map[string]interface{}),
   }
}
// Get
// @description "獲取配置map數(shù)據(jù)"
// @auth yezibin 2023-01-21 00:10:09
// @param name string "description"
// @return interface{} "description"
func (m *Mmap) Get(name string) interface{} {
   m.Mu.RLock()
   defer m.Mu.RUnlock()
   return m.Data[name]
}
 
// Set
// @description "批量設(shè)置map的值"
// @auth yezibin 2023-02-05 13:08:17
// @param data map[string]interface{} "description"
func (m *Mmap) Set(data map[string]interface{})  {
   m.Mu.Lock()
   defer m.Mu.Unlock()
   for k, v := range data {
      m.Data[k] = v
   }
 
}
// SetOne
// @description "設(shè)置配置map數(shù)據(jù)"
// @auth yezibin 2023-01-21 00:10:23
// @param key string "description"
// @param val string "description"
func (m *Mmap) SetOne(key, val string)  {
   m.Mu.Lock()
   defer m.Mu.Unlock()
   m.Data[key] = val
}

建議

1、如果屬于讀多寫(xiě)少的情況,盡量選擇讀寫(xiě)鎖來(lái)減少鎖住的范圍,從而提高讀寫(xiě)性能

2、這里推薦將需要用來(lái)讀寫(xiě)的map變量和鎖共同組建一個(gè)struct,這樣能保證讀和寫(xiě)上的是同一把讀寫(xiě)鎖,同時(shí)也方便整合對(duì)map變量的操作

3、分片加鎖

方案2中雖然加了讀寫(xiě)鎖,比加一把普通的鎖要性能高些,不過(guò)鎖的粒度還是大了些,當(dāng)高并發(fā)來(lái)襲時(shí),寫(xiě)的操作必然會(huì)阻塞讀的動(dòng)作,那么有沒(méi)有辦法將鎖住的范圍縮小一些呢

思路:如果給map里的每個(gè)元素加鎖,每次修改只是單個(gè)元素的鎖生效,其他沒(méi)改到的元素就正常讀,這樣鎖的粒度會(huì)更細(xì),這就是分片加鎖的原理

這種就是將一把“大”鎖拆成一把把小鎖,是一種空間換時(shí)間的方法

實(shí)現(xiàn)上,已經(jīng)有人實(shí)現(xiàn)了好用的具有分片鎖的map,庫(kù)地址:https://github.com/orcaman/concurrent-map

import (
   cmap "github.com/orcaman/concurrent-map"
   "sync"
)
// InitCmap
// @description "初始化分片鎖的map"
// @auth yezibin 2023-02-05 14:08:17
// @return *cmapConfig "description"
func InitCmap() *cmapConfig {
   return &cmapConfig{
      cmap.New(),
   }
}
 
// Set
// @description "批量往map寫(xiě)入元素"
// @auth yezibin 2023-02-05 14:10:02
// @param config map[string]interface{} "description"
func (c *cmapConfig) Set(config map[string]interface{})  {
   for k, v := range config{
      c.Cmap.Set(k, v)
   }
}
 
// Get
// @description "從map獲取元素"
// @auth yezibin 2023-02-05 14:10:22
// @param k string "description"
// @return interface{} "description"
func (c *cmapConfig) Get(k string) interface{} {
   v, ok := c.Cmap.Get(k)
   if ok {
      return v
   } else {
      return nil
   }
}

4、go的原生可并發(fā)map

最后還會(huì)跟大家介紹一個(gè)go原生庫(kù)里就有一個(gè)可并發(fā)讀寫(xiě)的map,這個(gè)放在sync庫(kù)

官方的文檔中指出,在以下兩個(gè)場(chǎng)景中使用 sync.Map,會(huì)比使用 map+RWMutex 的方式,性能要好得多:

1、只會(huì)增長(zhǎng)的緩存系統(tǒng)中,一個(gè) key 只寫(xiě)入一次而被讀很多次;

2、多個(gè) goroutine 為不相交的鍵集讀、寫(xiě)和重寫(xiě)鍵值對(duì)。

原理:sync.Map結(jié)構(gòu)里有兩個(gè)字段,一個(gè)read,一個(gè)dirty。dirty包含read的所有字段,新增字段是寫(xiě)在dirty上,有個(gè)miss變量用戶訪問(wèn)到read沒(méi)有,但是dirty有的數(shù)據(jù)次數(shù)

  • 空間換時(shí)間。通過(guò)冗余的兩個(gè)數(shù)據(jù)結(jié)構(gòu)(只讀的 read 字段、可寫(xiě)的 dirty),來(lái)減少加鎖對(duì)性能的影響。對(duì)只讀字段(read)的操作不需要加鎖。優(yōu)先從 read 字段讀取、更新、刪除,因?yàn)閷?duì) read 字段的讀取不需要鎖。
  • 動(dòng)態(tài)調(diào)整。miss 次數(shù)多了之后,將 dirty 數(shù)據(jù)提升為 read,避免總是從 dirty 中加鎖讀取。double-checking。加鎖之后先還要再檢查 read 字段,確定真的不存在才操作 dirty 字段。
  • 延遲刪除。刪除一個(gè)鍵值只是打標(biāo)記,只有在提升 dirty 字段為 read 字段的時(shí)候才清理刪除的數(shù)據(jù)。

示例代碼

type syncMapConfig struct {
   Smap sync.Map
}
// InitSmap
// @description "初始化sync.map"
// @auth yezibin 2023-02-05 15:43:08
// @return *syncMapConfig "description"
func InitSmap() *syncMapConfig {
   return &syncMapConfig{
      sync.Map{},
   }
}
// Set
// @description "批量寫(xiě)入map"
// @auth yezibin 2023-02-05 15:43:57
// @param config map[string]interface{} "description"
func (s *syncMapConfig) Set(config map[string]interface{})  {
   for k, v := range config {
      s.Smap.Store(k, v)
   }
}
// Get
// @description "從map里獲取數(shù)據(jù)"
// @auth yezibin 2023-02-05 15:44:09
// @param k string "description"
// @return interface{} "description"
func (s *syncMapConfig) Get(k string) interface{} {
   c, ok := s.Smap.Load(k)
   if ok {
      return c
   } else {
      return nil
   }
}

性能對(duì)比

上面說(shuō)了4種方法,處理用once這個(gè)包比較特殊(map只寫(xiě)一次,以后只讀),其他都是可讀寫(xiě)多次的,有可比性,那么2,3,4這三種方案的性能對(duì)比如何呢,哪種情況下該用哪種呢

標(biāo)注:下面數(shù)據(jù)對(duì)比,帶有相關(guān)字符的有如下含義

字符含義字符含義
Cmap使用了concurrent-map包WnR寫(xiě)和讀一樣多次
Smap使用了sync.Map包WnRMore讀多寫(xiě)少
Mmap使用RWMutexWMorenR寫(xiě)多讀少

當(dāng)并發(fā)=1000,對(duì)map是部分更新,且不是更新讀取的字段 

當(dāng)讀寫(xiě)一樣多的時(shí)候性能: sync.Map > concurrent-map > RWMutex map

當(dāng)讀多寫(xiě)少的時(shí)候性能:concurrent-map > sync.Map >  RWMutex map 

當(dāng)寫(xiě)多讀少的時(shí)候性能:sync.Map > concurrent-map > RWMutex map 

結(jié)論:當(dāng)高并發(fā)對(duì)map進(jìn)行讀寫(xiě)時(shí),如果寫(xiě)的字段和讀的字段錯(cuò)開(kāi)的時(shí)候

concurrent-map 在讀多寫(xiě)少的情況下有優(yōu)勢(shì),因?yàn)殒i的粒度小

sync.Map  在寫(xiě)多讀少的情況下有優(yōu)勢(shì),因?yàn)橛薪Y(jié)構(gòu)設(shè)計(jì)有優(yōu)勢(shì)

而讀寫(xiě)鎖因?yàn)榧渔i粒度大,導(dǎo)致高并發(fā)下性能都不是很好

當(dāng)并發(fā)=1000,對(duì)map是更新和讀取都是同一個(gè)字段

當(dāng)讀寫(xiě)一樣多的時(shí)候性能: sync.Map > RWMutex map > concurrent-map

當(dāng)讀多寫(xiě)少的時(shí)候性能:sync.Map > RWMutex map > concurrent-map 

當(dāng)寫(xiě)多讀少的時(shí)候性能:sync.Map > concurrent-map > RWMutex map 

在讀寫(xiě)都是同一個(gè)map字段的時(shí)候,sync.Map的結(jié)構(gòu)優(yōu)勢(shì)就凸顯了,因?yàn)閷?duì)讀和寫(xiě)是針對(duì)sync.Map 結(jié)構(gòu)里的read字段,且不加鎖;而其他兩個(gè)包都是會(huì)上鎖的

當(dāng)并發(fā)=10,對(duì)map是部分更新,且不是更新讀取的字段

當(dāng)讀寫(xiě)一樣多的時(shí)候性能: RWMutex map > sync.Map > concurrent-map

當(dāng)讀多寫(xiě)少的時(shí)候性能:RWMutex map > sync.Map > concurrent-map 

當(dāng)寫(xiě)多讀少的時(shí)候性能:RWMutex map > concurrent-map  > sync.Map

當(dāng)并發(fā)變低的情況下,RWMutex map的性能就好于其他兩種,主要原因是并發(fā)低,鎖的競(jìng)爭(zhēng)和阻塞情況變少,反而是結(jié)構(gòu)簡(jiǎn)單不需要占用大空間的RWMutex map形式要更好

當(dāng)并發(fā)=10,對(duì)map是更新和讀取都是同一個(gè)字段

當(dāng)讀寫(xiě)一樣多的時(shí)候性能: RWMutex map > sync.Map > concurrent-map

當(dāng)讀多寫(xiě)少的時(shí)候性能:RWMutex map > sync.Map > concurrent-map 

當(dāng)寫(xiě)多讀少的時(shí)候性能:RWMutex map  > sync.Map > concurrent-map

當(dāng)并發(fā)變低的情況下,RWMutex map的性能就好于其他兩種,主要原因是并發(fā)低,鎖的競(jìng)爭(zhēng)和阻塞情況變少,反而是結(jié)構(gòu)簡(jiǎn)單不需要占用大空間的RWMutex map形式要更好

最終結(jié)論

選用哪個(gè)方式,其實(shí)主要先看并發(fā)數(shù),其次看讀寫(xiě)模式,再來(lái)選擇使用哪種模式,以下表格是選用最優(yōu)解

讀多寫(xiě)少寫(xiě)多讀少
并發(fā)高concurrent-mapsync.Map
并發(fā)低RWMutex mapRWMutex map

到此這篇關(guān)于如何避免go的map競(jìng)態(tài)問(wèn)題的方法的文章就介紹到這了,更多相關(guān)go map競(jìng)態(tài)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Golang?pprof性能測(cè)試與分析講解

    Golang?pprof性能測(cè)試與分析講解

    剛開(kāi)始接觸go就遇到了一個(gè)內(nèi)存問(wèn)題,在進(jìn)行內(nèi)存分析的時(shí)候發(fā)現(xiàn)了一下比較好的工具,在此留下記錄,下面這篇文章主要給大家介紹了關(guān)于go性能分析工具pprof的性能測(cè)試,需要的朋友可以參考下
    2023-04-04
  • 一文詳解如何在Golang中實(shí)現(xiàn)JWT認(rèn)證與授權(quán)

    一文詳解如何在Golang中實(shí)現(xiàn)JWT認(rèn)證與授權(quán)

    在現(xiàn)代Web應(yīng)用中,安全性是一個(gè)非常重要的課題,JWT作為一種常用的認(rèn)證與授權(quán)機(jī)制,已被廣泛應(yīng)用于各種系統(tǒng)中,下面我們就來(lái)看看如何在Golang中實(shí)現(xiàn)JWT認(rèn)證與授權(quán)吧
    2025-03-03
  • Go代碼檢查的推薦工具及使用詳解

    Go代碼檢查的推薦工具及使用詳解

    這篇文章主要為大家介紹了Go代碼檢查的推薦工具及使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • go語(yǔ)言go?func(){select{}}()的用法

    go語(yǔ)言go?func(){select{}}()的用法

    本文主要介紹了go語(yǔ)言go?func(){select{}}()的用法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-02-02
  • Go中變量命名規(guī)則與實(shí)例

    Go中變量命名規(guī)則與實(shí)例

    命名規(guī)則涉及變量、常量、全局函數(shù)、結(jié)構(gòu)、接口、方法等的命名,下面這篇文章主要給大家介紹了關(guān)于Go中變量命名的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-01-01
  • grpc-go如何通過(guò)context傳遞額外數(shù)據(jù)

    grpc-go如何通過(guò)context傳遞額外數(shù)據(jù)

    metadata是grpc內(nèi)置的,用RPC服務(wù)傳遞http頭數(shù)據(jù),分in和out兩種,對(duì)應(yīng)的key都為一個(gè)空struct,這篇文章主要介紹了grpc-go通過(guò)context傳遞額外數(shù)據(jù),需要的朋友可以參考下
    2024-02-02
  • go json轉(zhuǎn)換實(shí)踐中遇到的坑

    go json轉(zhuǎn)換實(shí)踐中遇到的坑

    在使用 go 語(yǔ)言開(kāi)發(fā)過(guò)程中,經(jīng)常需要使用到 json 包來(lái)進(jìn)行 json 和 struct 的互相轉(zhuǎn)換,這篇文章主要介紹了go json轉(zhuǎn)換實(shí)踐中遇到的坑,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2018-12-12
  • golang包快速生成base64驗(yàn)證碼的方法

    golang包快速生成base64驗(yàn)證碼的方法

    這篇文章主要介紹了golang包快速生成base64驗(yàn)證碼的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • Go語(yǔ)言中strings和strconv包示例代碼詳解

    Go語(yǔ)言中strings和strconv包示例代碼詳解

    這篇文章主要介紹了Go語(yǔ)言中strings和strconv包示例代碼詳解 ,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2018-11-11
  • 使用go module導(dǎo)入本地包的方法教程詳解

    使用go module導(dǎo)入本地包的方法教程詳解

    go module 將是Go語(yǔ)言默認(rèn)的依賴管理工具。到今天 Go1.14 版本推出之后 Go modules 功能已經(jīng)被正式推薦在生產(chǎn)環(huán)境下使用了。本文重點(diǎn)給大家介紹如何使用 go module 導(dǎo)入本地包,感興趣的朋友一起看看吧
    2020-03-03

最新評(píng)論

泸定县| 常宁市| 图们市| 土默特左旗| 修武县| 肇东市| 秦安县| 葫芦岛市| 台南市| 南通市| 彰武县| 洪泽县| 汝城县| 莲花县| 东明县| 安图县| 兴和县| 城步| 社会| 连南| 景泰县| 屯留县| 灌云县| 阿勒泰市| 新郑市| 翼城县| 吴堡县| 江油市| 枝江市| 顺义区| 达尔| 简阳市| 巫溪县| 闵行区| 同德县| 永新县| 米泉市| 上杭县| 吐鲁番市| 桓台县| 定安县|