欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

golang中sync.Map并發(fā)創(chuàng)建、讀取問(wèn)題實(shí)戰(zhàn)記錄

背景:

公司主營(yíng)業(yè)務(wù):網(wǎng)站設(shè)計(jì)制作、網(wǎng)站設(shè)計(jì)、移動(dòng)網(wǎng)站開(kāi)發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭(zhēng)能力。成都創(chuàng)新互聯(lián)是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開(kāi)放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來(lái)的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來(lái)驚喜。成都創(chuàng)新互聯(lián)推出旌德免費(fèi)做網(wǎng)站回饋大家。

我們有一個(gè)用go做的項(xiàng)目,其中用到了zmq4進(jìn)行通信,一個(gè)簡(jiǎn)單的rpc過(guò)程,早期遠(yuǎn)端是使用一個(gè)map去做ip和具體socket的映射。

問(wèn)題

大概是這樣

struct SocketMap {
 sync.Mutex
 sockets map[string]*zmq4.Socket
}

然后調(diào)用的時(shí)候的代碼大概就是這樣的:

func (pushList *SocketMap) push(ip string, data []byte) {
 pushList.Lock()
 defer pushList.UnLock()
 socket := pushList.sockets[string]
 if socket == nil {
 socket := zmq4.NewSocket()
 //do some initial operation like connect
 pushList.sockets[ip] = socket
 }
 socket.Send(data)
}

相信大家都能看出問(wèn)題:當(dāng)push被并發(fā)訪問(wèn)的時(shí)候(事實(shí)上push會(huì)經(jīng)常被并發(fā)訪問(wèn)),由于這把大鎖的存在,同時(shí)只能有一個(gè)協(xié)程在臨界區(qū)工作,效率是會(huì)被大大降低的。

解決方案:會(huì)帶來(lái)crash的優(yōu)化

所以我們決定使用sync.Map來(lái)替代這個(gè)設(shè)計(jì),然后出了第一版代碼,寫(xiě)的非常簡(jiǎn)單,只做了簡(jiǎn)單的替換:

struct SocketMap {
 sockets sync.Map
}

func (pushList *SocketMap) push(ip string, data []byte) {
 var socket *zmq4.Socket 
 socketInter, ok = pushList.sockets.Load(ip)
 if !ok {
 socket = zmq4.NewSocket()
 //do some initial operation like connect
 pushList.sockets.Store(ip, socket)
 } else {
 socket = socketInter.(*zmq4.Socket)
 }
 socket.Send(data)
}

乍一看似乎沒(méi)什么問(wèn)題?但是跑起來(lái)總是爆炸,然后一看log,提示有個(gè)非法地址。后來(lái)在github上才看到,zmq4.Socket不是線程安全的。上面的代碼恰恰會(huì)造成多個(gè)線程同時(shí)拿到socket實(shí)例,然后就crash了。

解決方案2: 加一把鎖也擋不住的沖突

然后怎么辦呢?看來(lái)也只能加鎖了,不過(guò)這次加鎖不能加到整個(gè)map上,否則還會(huì)有性能問(wèn)題,那就考慮減小鎖的粒度吧,使用鎖包裝socket。這個(gè)時(shí)候我們的代碼也就呼之欲出了:

struct SocketMutex{
 sync.Mutex
 socket *zmq4.Socket
}
struct SocketMap {
 sockets sync.Map
}

func (pushList *SocketMap) push(ip string, data []byte) {
 var socket *SocketMutex 
 socketInter, ok = pushList.sockets.Load(ip)
 if !ok {
 socket = &{
  socket: zmq4.NewSocket()
 }
 //do some initial operation like connect
 pushList.sockets.Store(ip, newSocket)
 } else {
 socket = socketInter.(*SocketMutex)
 }
 socket.Lock()
 defer socket.Unlock()
 socket.socket.Send(data)
}

但是這樣還是有問(wèn)題,相信經(jīng)驗(yàn)比較豐富的老哥一眼就能看出來(lái),問(wèn)題處在socketInter, ok = pushList.sockets.Load(ip)這行代碼上,如果map中沒(méi)有這個(gè)值,且有多個(gè)協(xié)程同時(shí)訪問(wèn)到這行代碼,顯然這幾個(gè)協(xié)程的ok都會(huì)置為false,然后都進(jìn)入第一個(gè)if代碼塊,創(chuàng)建多個(gè)socket實(shí)例,并且爭(zhēng)相覆蓋原有值。

單純解決這個(gè)問(wèn)題也很簡(jiǎn)單,就是使用sync.Map.LoadOrStore(key interface{}, value interface{}) (v interface{}, loaded bool)這個(gè)api,來(lái)原子地去做讀寫(xiě)。

然而這還沒(méi)完,我們的寫(xiě)入新值的操作不光是調(diào)用一個(gè)api創(chuàng)建socket就完了,還要有一系列的初始化操作,我們必須保證在初始化完成之前,其他通過(guò)Load拿到這個(gè)實(shí)例的協(xié)程無(wú)法真正訪問(wèn)socket實(shí)例。

這時(shí)候顯然sync.Map自帶的機(jī)制已經(jīng)無(wú)法解決這個(gè)問(wèn)題了,那么我們必須尋求其他的手段,要么鎖,要么就sync.WaitGroup或者whatever的其他什么東西。

解決方案3: 閉包帶來(lái)的神奇體驗(yàn)

后來(lái)經(jīng)大佬指點(diǎn),我在encoder.go中看到了這么一段代碼:

 func typeEncoder(t reflect.Type) encoderFunc {     
 if fi, ok := encoderCache.Load(t); ok {     
  return fi.(encoderFunc)      
 }          
          
 // To deal with recursive types, populate the map with an   
 // indirect func before we build it. This type waits on the  
 // real func (f) to be ready and then calls it. This indirect  
 // func is only used for recursive types.     
 var (         
  wg sync.WaitGroup       
  f encoderFunc        
 )          
 wg.Add(1)         
 fi, loaded := encoderCache.LoadOrStore(t, encoderFunc(func(e *encodeState, v reflect.Value, opts encOpts) {
  wg.Wait()        
  f(e, v, opts)        
 }))         
 if loaded {        
  return fi.(encoderFunc)      
 }          
           
 // Compute the real encoder and replace the indirect func with it.  
 f = newTypeEncoder(t, true)      
 wg.Done()         
 encoderCache.Store(t, f)       
 return f         
 }  

豁然開(kāi)朗,我們可以在sync.Map中存放一個(gè)閉包函數(shù),然后在閉包函數(shù)中等待本地的sync.WaitGroup完成再返回實(shí)例。于是最終的代碼也就成型了。

struct SocketMutex{
 sync.Mutex
 socket *zmq4.Socket
}
struct SocketMap {
 sockets sync.Map
}

func (pushList *SocketMap) push(ip string, data []byte) {
 type SocketFunc func()*SocketMutex
 var (
  socket *SocketMutex
  w sync.WaitGroup
 )
 socket = &SocketMutex {
  socket : zmq4.NewSocket()
 } 
 w.Add(1)
 socketf, ok = pushList.sockets.LoadOrStore(ip, SocketFunc(func()*SocketMutex) {
  w.Wait()
  return socket
 })
 if !ok {
  socket = &{
   socket: zmq4.NewSocket()
  }
  //do some initial operation like connect
  w.Done()
 } else {
  socket = socketInter.(*SockeFunc)()
 }
 socket.Lock()
 defer socket.Unlock()
 socket.socket.Send(data)
}

總結(jié):

并發(fā)代碼中的競(jìng)爭(zhēng)問(wèn)題,每一行代碼的重入性都要深思熟慮啊。

總的來(lái)說(shuō)要保持以下幾個(gè)準(zhǔn)則:

(1) 不可重入訪問(wèn)的系統(tǒng)資源,如socketfd, filefd,signalfd(事實(shí)上大多數(shù)這種系統(tǒng)資源都是不可重入的)等,在使用無(wú)鎖結(jié)構(gòu)的容器、讀寫(xiě)鎖封裝的容器時(shí),需要給每個(gè)資源單獨(dú)加鎖或者使用其他手段保證系統(tǒng)資源在臨界區(qū)受到有效保護(hù)。

(2)如果有讀取,如果為空則寫(xiě)入的邏輯,需要使用能提供原子性保證的LoadOrSave調(diào)用,或者沒(méi)有的話,自己實(shí)現(xiàn)也要保證讀取和寫(xiě)入過(guò)程整體的原子性;防止并發(fā)訪問(wèn)Load調(diào)用時(shí),多個(gè)線程都返回否而創(chuàng)建多個(gè)實(shí)例,然后在Save的時(shí)候又互相覆蓋。——這個(gè)原則不光對(duì)成員是系統(tǒng)資源的時(shí)候生效,如果存放的是其他東西也同樣適用。

(3)如果資源創(chuàng)建完畢,還需要其他的初始化過(guò)程,則可以考慮在容器內(nèi)放置閉包,初始化過(guò)程使用sync.WaitGroup保護(hù),在閉包中調(diào)用Wait方法等待初始化完成再給其他線程返回初始化好的實(shí)例。而初始化過(guò)程完成后,可以置換閉包函數(shù),不再調(diào)用Wait方法,來(lái)減少可能的開(kāi)銷。

好了,以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)創(chuàng)新互聯(lián)的支持。

當(dāng)前題目:golang中sync.Map并發(fā)創(chuàng)建、讀取問(wèn)題實(shí)戰(zhàn)記錄
當(dāng)前網(wǎng)址:http://chinadenli.net/article48/ppcohp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供外貿(mào)建站、網(wǎng)站制作網(wǎng)站維護(hù)、建站公司、網(wǎng)站內(nèi)鏈全網(wǎng)營(yíng)銷推廣

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

綿陽(yáng)服務(wù)器托管
久久经典一区二区三区| 日韩精品中文字幕在线视频| 麻豆亚州无矿码专区视频| 热久久这里只有精品视频| 91精品国产av一区二区| 精产国品一二三区麻豆| 色婷婷久久五月中文字幕| 黄片免费播放一区二区| 麻豆欧美精品国产综合久久| 欧美熟妇喷浆一区二区| 一区二区福利在线视频| 亚洲一区二区三区四区| 一级片黄色一区二区三区| 五月婷婷亚洲综合一区| 国产美女精品午夜福利视频| 老熟妇2久久国内精品| 女人高潮被爽到呻吟在线观看| 国产成人免费激情视频| 成人精品视频在线观看不卡| 香蕉网尹人综合在线观看| 成人精品一区二区三区在线| 亚洲国产91精品视频| 国产韩国日本精品视频| 久久精品蜜桃一区二区av| 久久精品少妇内射毛片| 欧美加勒比一区二区三区| 日本人妻精品有码字幕| 日韩无套内射免费精品| 日韩欧美国产精品中文字幕| 成人精品一区二区三区在线| 日本av在线不卡一区| 福利在线午夜绝顶三级| 内射精子视频欧美一区二区| 日韩精品人妻少妇一区二区| 亚洲欧美日韩色图七区| 中文字幕亚洲精品人妻| 久久精品国产亚洲熟女| 日韩免费国产91在线| 美女被后入福利在线观看| 日本高清中文精品在线不卡| 精品欧美日韩一二三区|