慎用golang中的map霞篡,特別是在并發(fā)操作中

簡單介紹

map數(shù)據(jù)類型在很多語言中都有,是一個(gè)key端逼,value形式的hash表朗兵,從而將key,value進(jìn)行一一映射顶滩,進(jìn)行快速查找余掖、添加、刪除等操作礁鲁。在Go語言中也不例外盐欺,提供了map數(shù)據(jù)結(jié)構(gòu)類型。

內(nèi)建map切忌開箱即用

Golang中仅醇,map是引用類型找田,如指針切片一樣,通過下面的代碼聲明后指向的是nil着憨。這點(diǎn)在golang官方文檔中也說明了墩衙,所以千萬別直接聲明后就使用,開始可能經(jīng)常會(huì)犯下面的錯(cuò):

var m map[string]string
m["result"] = "result"

上面的第一行代碼并沒有對map進(jìn)行一個(gè)初始化甲抖,而卻對其進(jìn)行寫入操作漆改,就是對空指針的引用,這將會(huì)造成一個(gè)painc准谚。所以挫剑,得記得用make函數(shù)對其進(jìn)行分配內(nèi)存和初始化:

m := make(map[string]string)
m["result"] = "result"

golang中的map并不是并發(fā)安全的

經(jīng)常使用map,平時(shí)用著也很爽柱衔,但是突然某天流量上來了樊破,程序不知不覺就掛了,還不清楚是怎么回事唆铐,明明以前用著好好的呀哲戚。所以有些好習(xí)慣在剛開始就養(yǎng)成,比如斷言檢查艾岂,并發(fā)安全考慮等顺少。

map縱然很好用,但也得謹(jǐn)慎惯驼≌钍荩或許很多人還不知道奈籽,golang內(nèi)建map其實(shí)并不是并發(fā)安全的伟阔,下面我自定義了一個(gè)結(jié)構(gòu)體,賦其map屬性埂息,給其綁定Get肮塞,Set方法方便操作逾一。請看下面代碼:

// M
type M struct {
    Map    map[string]string
}

// Set ...
func (m *M) Set(key, value string) {
    m.Map[key] = value
}

// Get ...
func (m *M) Get(key string) string {
    return m.Map[key]
}

上面的代碼中几蜻,給一個(gè)結(jié)構(gòu)體賦予了一個(gè)map屬性喇潘,且綁定了兩個(gè)方法,進(jìn)行讀寫操作入蛆。當(dāng)你在寫了一個(gè)test在單個(gè)goroutine中跑的時(shí)候或許沒什么問題响蓉,甚至10個(gè)goroutine硕勿,20個(gè)都還正常哨毁。

// TestMap  ...
func TestMap(t *testing.T) {
    c := helper.M{Map: make(map[string]string)}
    wg := sync.WaitGroup{}
    for i := 0; i < 21; i++ {
        wg.Add(1)
        go func(n int) {
            k, v := strconv.Itoa(n), strconv.Itoa(n)
            c.Set(k, v)
            t.Logf("k=:%v,v:=%v\n", k, c.Get(k))
            wg.Done()
        }(i)
    }
    wg.Wait()
    t.Log("ok finished.")
}

然而當(dāng)你你再添加的時(shí)候,goroutine再增加的時(shí)候源武,會(huì)報(bào)下面的錯(cuò),也就是map并發(fā)寫入出錯(cuò).

fatal error: concurrent map writes

goroutine 75 [running]:
runtime.throw(0x13b2011, 0x15)

需要說明一點(diǎn)的是扼褪,在Http請求時(shí),我們通常對參數(shù)封裝粱栖,encode话浇,可能會(huì)調(diào)用golang自帶的url.Values{},通過讀源碼可以發(fā)現(xiàn)也是線程不安全的闹究。

如何解決

其實(shí)要解決上面的問題也不難幔崖,出錯(cuò)原因golang已經(jīng)寫得很清楚了,concurrent map writes,并發(fā)寫map異常渣淤,這個(gè)時(shí)候肯定想的是并發(fā)操作上能不能解決赏寇。

很顯然,我們可以用鎖機(jī)制解決上面的問題价认。我們將上面的map結(jié)構(gòu)改成如下:

// M
type M struct {
    Map    map[string]string
    lock sync.RWMutex // 加鎖
}

// Set ...
func (m *M) Set(key, value string) {
    m.lock.Lock()
    defer m.lock.Unlock()
    m.Map[key] = value
}

// Get ...
func (m *M) Get(key string) string {
    return m.Map[key]
}

在上面的代碼中嗅定,我們引入了鎖機(jī)制操作,從而保證了map在多個(gè)goroutine中的安全用踩。這時(shí)再執(zhí)行我們的test會(huì)發(fā)現(xiàn)其正常輸出渠退。

=== RUN   TestMap
--- PASS: TestMap (0.00s)
    map_test.go:27: k=:5,v:=5
    map_test.go:27: k=:17,v:=17
    map_test.go:27: k=:20,v:=20
    map_test.go:27: k=:19,v:=19
    map_test.go:27: k=:6,v:=6

或許你可以嘗試下sync.Map

golang中的sync.Map是并發(fā)安全的,其實(shí)也就是sync包中g(shù)olang自定義的一個(gè)名叫Map的結(jié)構(gòu)體脐彩。結(jié)構(gòu)體原型如下:

type Map struct {
   mu Mutex
   read atomic.Value // readOnly
   dirty map[interface{}]*entry
   misses int
}

可以看見有 Mutex碎乃,很顯然也是用了鎖機(jī)制的,從而來保證了并發(fā)安全惠奸。該包中的Map提供了Store荠锭、Load、Delete晨川、Range等操证九。并且sync包中的Map是開箱可用的删豺,也即是聲明后就可以直接使用,如下:

var m sync.Map
m.Store("method", "eth_getBlockByHash")
value, ok := m.Load("method")
t.Logf("value=%v,ok=%v\n",value,ok)

當(dāng)然也可以用Range遍歷

// TestMap  ...
func TestMap(t *testing.T) {
    var m sync.Map
    m.Store("method", "eth_getBlockByHash")
    m.Store("jsonrpc", "2.0")
    //value, ok := m.Load("method")
    //t.Logf("value=%v,ok=%v\n", value, ok)
    //f := func(key, value string) {
    //
    //}
    //f("method", "eth_getBlockByHash")
    m.Range(func(key, value interface{}) bool {
        t.Logf("range k:%v,v=%v\n", key, value)
        return true
    })
}

結(jié)果如下:

=== RUN   TestMap
--- PASS: TestMap (0.00s)
    map_test.go:43: range k:jsonrpc,v=2.0
    map_test.go:43: range k:method,v=eth_getBlockByHash
PASS

一個(gè)游蕩于文藝界與IT界的草根人物愧怜。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末呀页,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子拥坛,更是在濱河造成了極大的恐慌蓬蝶,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猜惋,死亡現(xiàn)場離奇詭異丸氛,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)著摔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門缓窜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谍咆,你說我怎么就攤上這事禾锤。” “怎么了摹察?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵恩掷,是天一觀的道長。 經(jīng)常有香客問我供嚎,道長黄娘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任克滴,我火速辦了婚禮逼争,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘偿曙。我一直安慰自己氮凝,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布望忆。 她就那樣靜靜地躺著罩阵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪启摄。 梳的紋絲不亂的頭發(fā)上稿壁,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音歉备,去河邊找鬼傅是。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的喧笔。 我是一名探鬼主播帽驯,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼书闸!你這毒婦竟也來了尼变?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤浆劲,失蹤者是張志新(化名)和其女友劉穎嫌术,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體牌借,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡度气,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了膨报。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片磷籍。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖丙躏,靈堂內(nèi)的尸體忽然破棺而出择示,到底是詐尸還是另有隱情束凑,我是刑警寧澤晒旅,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站汪诉,受9級(jí)特大地震影響废恋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜扒寄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一鱼鼓、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧该编,春花似錦迄本、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至于樟,卻和暖如春公条,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背迂曲。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國打工靶橱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓关霸,卻偏偏與公主長得像传黄,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子队寇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容