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

深入探討Go語言中的map是否是并發(fā)安全以及解決方法

 更新時(shí)間:2023年05月29日 08:29:12   作者:yongxinz  
這篇文章主要來和大家探討?Go?語言中的?map?是否是并發(fā)安全的,并提供三種方案來解決并發(fā)問題,文中的示例代碼講解詳細(xì),需要的可以參考一下

Go 語言中的 map 是一個(gè)非常常用的數(shù)據(jù)結(jié)構(gòu),它允許我們快速地存儲(chǔ)和檢索鍵值對(duì)。然而,在并發(fā)場(chǎng)景下使用 map 時(shí),還是有一些問題需要注意的。

本文將探討 Go 語言中的 map 是否是并發(fā)安全的,并提供三種方案來解決并發(fā)問題。

先來回答一下題目的問題,答案就是并發(fā)不安全。

看一段代碼示例,當(dāng)兩個(gè) goroutine 同時(shí)對(duì)同一個(gè) map 進(jìn)行寫操作時(shí),會(huì)發(fā)生什么?

package?main

import?"sync"

func?main()?{
????m?:=?make(map[string]int)
????m["foo"]?=?1

????var?wg?sync.WaitGroup
????wg.Add(2)

????go?func()?{
????????for?i?:=?0;?i?<?1000;?i++?{
????????????m["foo"]++
????????}
????????wg.Done()
????}()

????go?func()?{
????????for?i?:=?0;?i?<?1000;?i++?{
????????????m["foo"]++
????????}
????????wg.Done()
????}()

????wg.Wait()
}

在這個(gè)例子中,我們可以看到,兩個(gè) goroutine 將嘗試同時(shí)對(duì) map 進(jìn)行寫入。運(yùn)行這個(gè)程序時(shí),我們將看到一個(gè)錯(cuò)誤:

fatal error: concurrent map writes

也就是說,在并發(fā)場(chǎng)景下,這樣操作 map 是不行的。

為什么是不安全的

因?yàn)樗?strong>沒有內(nèi)置的鎖機(jī)制來保護(hù)多個(gè) goroutine 同時(shí)對(duì)其進(jìn)行讀寫操作。

當(dāng)多個(gè) goroutine 同時(shí)對(duì)同一個(gè) map 進(jìn)行讀寫操作時(shí),就會(huì)出現(xiàn)數(shù)據(jù)競(jìng)爭(zhēng)和不一致的結(jié)果。

就像上例那樣,當(dāng)兩個(gè) goroutine 同時(shí)嘗試更新同一個(gè)鍵值對(duì)時(shí),最終的結(jié)果可能取決于哪個(gè) goroutine 先完成了更新操作。這種不確定性可能會(huì)導(dǎo)致程序出現(xiàn)錯(cuò)誤或崩潰。

Go 語言團(tuán)隊(duì)沒有將 map 設(shè)計(jì)成并發(fā)安全的,是因?yàn)檫@樣會(huì)增加程序的開銷并降低性能。

如果 map 內(nèi)置了鎖機(jī)制,那么每次訪問 map 時(shí)都需要進(jìn)行加鎖和解鎖操作,這會(huì)增加程序的運(yùn)行時(shí)間并降低性能。

此外,并不是所有的程序都需要在并發(fā)場(chǎng)景下使用 map,因此將鎖機(jī)制內(nèi)置到 map 中會(huì)對(duì)那些不需要并發(fā)安全的程序造成不必要的開銷。

在實(shí)際使用過程中,開發(fā)人員可以根據(jù)程序的需求來選擇是否需要保證 map 的并發(fā)安全性,從而在性能和安全性之間做出權(quán)衡。

如何并發(fā)安全

接下來介紹三種并發(fā)安全的方式:

  • 讀寫鎖
  • 分片加鎖
  • sync.Map

加讀寫鎖

第一種方法是使用讀寫鎖,這是最容易想到的一種方式。在讀操作時(shí)加讀鎖,在寫操作時(shí)加寫鎖。

package?main

import?(
????"fmt"
????"sync"
)

type?SafeMap?struct?{
????sync.RWMutex
????Map?map[string]string
}

func?NewSafeMap()?*SafeMap?{
????sm?:=?new(SafeMap)
????sm.Map?=?make(map[string]string)
????return?sm
}

func?(sm?*SafeMap)?ReadMap(key?string)?string?{
????sm.RLock()
????value?:=?sm.Map[key]
????sm.RUnlock()
????return?value
}

func?(sm?*SafeMap)?WriteMap(key?string,?value?string)?{
????sm.Lock()
????sm.Map[key]?=?value
????sm.Unlock()
}

func?main()?{
????safeMap?:=?NewSafeMap()

????var?wg?sync.WaitGroup

????//?啟動(dòng)多個(gè)goroutine進(jìn)行寫操作
????for?i?:=?0;?i?<?10;?i++?{
????????wg.Add(1)
????????go?func(i?int)?{
????????????defer?wg.Done()
????????????safeMap.WriteMap(fmt.Sprintf("name%d",?i),?fmt.Sprintf("John%d",?i))
????????}(i)
????}

????wg.Wait()

????//?啟動(dòng)多個(gè)goroutine進(jìn)行讀操作
????for?i?:=?0;?i?<?10;?i++?{
????????wg.Add(1)
????????go?func(i?int)?{
????????????defer?wg.Done()
????????????fmt.Println(safeMap.ReadMap(fmt.Sprintf("name%d",?i)))
????????}(i)
????}

????wg.Wait()
}

在這個(gè)示例中,我們定義了一個(gè) SafeMap 結(jié)構(gòu)體,它包含一個(gè) sync.RWMutex 和一個(gè) map[string]string。

定義了兩個(gè)方法:ReadMap 和 WriteMap。在 ReadMap 方法中,我們使用讀鎖來保護(hù)對(duì) map 的讀取操作。在 WriteMap 方法中,我們使用寫鎖來保護(hù)對(duì) map 的寫入操作。

在 main 函數(shù)中,我們啟動(dòng)了多個(gè) goroutine 來進(jìn)行讀寫操作,這些操作都是安全的。

分片加鎖

上例中通過對(duì)整個(gè) map 加鎖來實(shí)現(xiàn)需求,但相對(duì)來說,鎖會(huì)大大降低程序的性能,那如何優(yōu)化呢?其中一個(gè)優(yōu)化思路就是降低鎖的粒度,不對(duì)整個(gè) map 進(jìn)行加鎖。

這種方法是分片加鎖,將這個(gè) map 分成 n 塊,每個(gè)塊之間的讀寫操作都互不干擾,從而降低沖突的可能性。

package?main

import?(
????"fmt"
????"sync"
)

const?N?=?16

type?SafeMap?struct?{
????maps??[N]map[string]string
????locks?[N]sync.RWMutex
}

func?NewSafeMap()?*SafeMap?{
????sm?:=?new(SafeMap)
????for?i?:=?0;?i?<?N;?i++?{
????????sm.maps[i]?=?make(map[string]string)
????}
????return?sm
}

func?(sm?*SafeMap)?ReadMap(key?string)?string?{
????index?:=?hash(key)?%?N
????sm.locks[index].RLock()
????value?:=?sm.maps[index][key]
????sm.locks[index].RUnlock()
????return?value
}

func?(sm?*SafeMap)?WriteMap(key?string,?value?string)?{
????index?:=?hash(key)?%?N
????sm.locks[index].Lock()
????sm.maps[index][key]?=?value
????sm.locks[index].Unlock()
}

func?hash(s?string)?int?{
????h?:=?0
????for?i?:=?0;?i?<?len(s);?i++?{
????????h?=?31*h?+?int(s[i])
????}
????return?h
}

func?main()?{
????safeMap?:=?NewSafeMap()

????var?wg?sync.WaitGroup

????//?啟動(dòng)多個(gè)goroutine進(jìn)行寫操作
????for?i?:=?0;?i?<?10;?i++?{
????????wg.Add(1)
????????go?func(i?int)?{
????????????defer?wg.Done()
????????????safeMap.WriteMap(fmt.Sprintf("name%d",?i),?fmt.Sprintf("John%d",?i))
????????}(i)
????}

????wg.Wait()

????//?啟動(dòng)多個(gè)goroutine進(jìn)行讀操作
????for?i?:=?0;?i?<?10;?i++?{
????????wg.Add(1)
????????go?func(i?int)?{
????????????defer?wg.Done()
????????????fmt.Println(safeMap.ReadMap(fmt.Sprintf("name%d",?i)))
????????}(i)
????}

????wg.Wait()
}

在這個(gè)示例中,我們定義了一個(gè) SafeMap 結(jié)構(gòu)體,它包含一個(gè)長度為 N 的 map 數(shù)組和一個(gè)長度為 N 的鎖數(shù)組。

定義了兩個(gè)方法:ReadMap 和 WriteMap。在這兩個(gè)方法中,我們都使用了一個(gè) hash 函數(shù)來計(jì)算 key 應(yīng)該存儲(chǔ)在哪個(gè) map 中。然后再對(duì)這個(gè) map 進(jìn)行讀寫操作。

在 main 函數(shù)中,我們啟動(dòng)了多個(gè) goroutine 來進(jìn)行讀寫操作,這些操作都是安全的。

有一個(gè)開源項(xiàng)目 orcaman/concurrent-map 就是通過這種思想來做的,感興趣的同學(xué)可以看看。

sync.Map

最后,在內(nèi)置的 sync 包中(Go 1.9+)也有一個(gè)線程安全的 map,通過將讀寫分離的方式實(shí)現(xiàn)了某些特定場(chǎng)景下的性能提升。

package?main

import?(
????"fmt"
????"sync"
)

func?main()?{
????var?m?sync.Map
????var?wg?sync.WaitGroup

????//?啟動(dòng)多個(gè)goroutine進(jìn)行寫操作
????for?i?:=?0;?i?<?10;?i++?{
????????wg.Add(1)
????????go?func(i?int)?{
????????????defer?wg.Done()
????????????m.Store(fmt.Sprintf("name%d",?i),?fmt.Sprintf("John%d",?i))
????????}(i)
????}

????wg.Wait()

????//?啟動(dòng)多個(gè)goroutine進(jìn)行讀操作
????for?i?:=?0;?i?<?10;?i++?{
????????wg.Add(1)
????????go?func(i?int)?{
????????????defer?wg.Done()
????????????v,?_?:=?m.Load(fmt.Sprintf("name%d",?i))
????????????fmt.Println(v.(string))
????????}(i)
????}

????wg.Wait()
}

有了官方的支持,代碼瞬間少了很多,使用起來方便多了。

在這個(gè)示例中,我們使用了內(nèi)置的 sync.Map 類型來存儲(chǔ)鍵值對(duì),使用 Store 方法來存儲(chǔ)鍵值對(duì),使用 Load 方法來獲取鍵值對(duì)。

在 main 函數(shù)中,我們啟動(dòng)了多個(gè) goroutine 來進(jìn)行讀寫操作,這些操作都是安全的。

總結(jié)

Go 語言中的 map 本身并不是并發(fā)安全的。

在多個(gè) goroutine 同時(shí)訪問同一個(gè) map 時(shí),可能會(huì)出現(xiàn)并發(fā)不安全的現(xiàn)象。這是因?yàn)?Go 語言中的 map 并沒有內(nèi)置鎖來保護(hù)對(duì)map的訪問。

盡管如此,我們?nèi)匀豢梢允褂靡恍┓椒▉韺?shí)現(xiàn) map 的并發(fā)安全。

一種方法是使用讀寫鎖,在讀操作時(shí)加讀鎖,在寫操作時(shí)加寫鎖。

另一種方法是分片加鎖,將這個(gè) map 分成 n 塊,每個(gè)塊之間的讀寫操作都互不干擾,從而降低沖突的可能性。

此外,在內(nèi)置的 sync 包中(Go 1.9+)也有一個(gè)線程安全的 map,它通過將讀寫分離的方式實(shí)現(xiàn)了某些特定場(chǎng)景下的性能提升。

到此這篇關(guān)于深入探討Go語言中的map是否是并發(fā)安全以及解決方法的文章就介紹到這了,更多相關(guān)Go語言map內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 一文詳解GO如何實(shí)現(xiàn)Redis的AOF持久化

    一文詳解GO如何實(shí)現(xiàn)Redis的AOF持久化

    這篇文章主要為大家詳細(xì)介紹了GO如何實(shí)現(xiàn)Redis的AOF持久化的,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以了解一下
    2023-03-03
  • 利用Go語言搭建WebSocket服務(wù)端方法示例

    利用Go語言搭建WebSocket服務(wù)端方法示例

    這篇文章主要給大家介紹了利用Go語言搭建WebSocket服務(wù)端方法,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們可以參考借鑒,下面來一起看看吧。
    2017-04-04
  • golang操作rocketmq的示例代碼

    golang操作rocketmq的示例代碼

    這篇文章主要介紹了golang操作rocketmq的示例代碼,代碼簡單易懂,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-04-04
  • Golang中map的深入探究

    Golang中map的深入探究

    Go中Map是一個(gè)KV對(duì)集合,下面這篇文章主要給大家介紹了關(guān)于Golang中map探究的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-09-09
  • 一文帶你了解Golang中的并發(fā)性

    一文帶你了解Golang中的并發(fā)性

    并發(fā)是一個(gè)很酷的話題,一旦你掌握了它,就會(huì)成為一筆巨大的財(cái)富。所以本文就來和大家一起來聊聊Golang中的并發(fā)性,感興趣的可以了解一下
    2023-03-03
  • 在golang中使用Sync.WaitGroup解決等待的問題

    在golang中使用Sync.WaitGroup解決等待的問題

    這篇文章主要介紹了在golang中使用Sync.WaitGroup解決等待的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • 詳解如何在Golang中執(zhí)行shell命令

    詳解如何在Golang中執(zhí)行shell命令

    這篇文章主要為大家詳細(xì)介紹了在 golang 中執(zhí)行 shell 命令的多種方法和場(chǎng)景,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-02-02
  • Go標(biāo)準(zhǔn)庫Flag庫和Log庫的使用

    Go標(biāo)準(zhǔn)庫Flag庫和Log庫的使用

    本文主要介紹了Go標(biāo)準(zhǔn)庫Flag庫和Log庫的使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-05-05
  • go語言接口之接口值舉例詳解

    go語言接口之接口值舉例詳解

    接口是一種抽象類型,是對(duì)其他類型行為的概括與抽象,從語法角度來看,接口是一組方法定義的集合,下面這篇文章主要給大家介紹了關(guān)于go語言接口之接口值的相關(guān)資料,文章通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-06-06
  • golang實(shí)現(xiàn)對(duì)JavaScript代碼混淆

    golang實(shí)現(xiàn)對(duì)JavaScript代碼混淆

    在Go語言中,你可以使用一些工具來混淆JavaScript代碼,一個(gè)常用的工具是Terser,它可以用于壓縮和混淆JavaScript代碼,你可以通過Go語言的`os/exec`包來調(diào)用Terser工具,本文給通過一個(gè)簡單的示例給大家介紹一下,感興趣的朋友可以參考下
    2024-01-01

最新評(píng)論