數(shù)據(jù)競爭和內(nèi)存重分配Golang slice并發(fā)不安全問題解決
Golang 中的 slice 為什么是并發(fā)不安全的?
一、并發(fā)不安全的
在Go語言中,slice是并發(fā)不安全的,主要有以下兩個原因:數(shù)據(jù)競爭、內(nèi)存重分配。
數(shù)據(jù)競爭:slice底層的結(jié)構(gòu)體包含一個指向底層數(shù)組的指針和該數(shù)組的長度,當(dāng)多個協(xié)程并發(fā)訪問同一個slice時,有可能會出現(xiàn)數(shù)據(jù)競爭的問題。例如,一個協(xié)程在修改slice的長度,而另一個協(xié)程同時在讀取或修改slice的內(nèi)容。
內(nèi)存重分配:在向slice中追加元素時,可能會觸發(fā)slice的擴(kuò)容操作,在這個過程中,如果有其他協(xié)程訪問了slice,就會導(dǎo)致指向底層數(shù)組的指針出現(xiàn)異常。
二、并發(fā)場景
多個協(xié)程同時向 slice 追加元素,會有一部分元素被追加到了舊的底層數(shù)組里,最終 slice 的長度小于目標(biāo)值。
func main() { a := make([]int, 0) for i := 0; i < 10000; i++ { go func(i int) { a = append(a, i) }(i) } fmt.Println(len(a)) // 9015 < 10000 }
三、實(shí)現(xiàn) slice 并發(fā)安全
要實(shí)現(xiàn) slice 并發(fā)安全,有兩種方法:加互斥鎖、使用channel串行化操作。
方式一:使用互斥鎖 sync.Mutex
追加元素之前調(diào)用 Lock() 函數(shù)加鎖,追加完后,調(diào)用 Unlock() 解鎖。
func main() { var lock sync.Mutex //互斥鎖 a := make([]int, 0) var wg sync.WaitGroup for i := 0; i < 10000; i++ { wg.Add(1) go func(i int) { defer wg.Done() lock.Lock() defer lock.Unlock() a = append(a, i) }(i) } wg.Wait() fmt.Println(len(a)) // equal 10000 }
最終 slice 的長度等于目標(biāo)值。
方式二:使用channel串行化操作
生產(chǎn)者生產(chǎn)元素,發(fā)送到通道中,消費(fèi)者從通道中接收元素,追加到 slice 中。使用無緩沖通道,接收方、發(fā)送方必須同時存在,負(fù)責(zé)任意一方都會阻塞。
func main() { buffer := make(chan int) a := make([]int, 0) // 消費(fèi)者 go func() { for v := range buffer { a = append(a, v) } }() // 生產(chǎn)者 var wg sync.WaitGroup for i := 0; i < 10000; i++ { wg.Add(1) go func(i int) { defer wg.Done() buffer <- i }(i) } wg.Wait() fmt.Println(len(a)) // equal 10000 }
最終 slice 的長度等于目標(biāo)值。
兩種方式的比較
加互斥鎖適合于對性能要求不高的場景,畢竟鎖的粒度太大,這種方式屬于通過共享內(nèi)存來實(shí)現(xiàn)通信。channle 適合于對性能要求大的場景,channle 就是專用于 goroutine 間通信的,這種方式屬于通過通信來實(shí)現(xiàn)共享內(nèi)存。
以上就是數(shù)據(jù)競爭和內(nèi)存重分配Golang slice并發(fā)不安全問題解決的詳細(xì)內(nèi)容,更多關(guān)于Golang slice并發(fā)安全的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
MacOS中 VSCode 安裝 GO 插件失敗問題的快速解決方法
這篇文章主要介紹了MacOS中 VSCode 安裝 GO 插件失敗問題的快速解決方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05詳解Go中如何進(jìn)行進(jìn)行內(nèi)存優(yōu)化和垃圾收集器管理
這篇文章主要為大家詳細(xì)介紹了Go中如何進(jìn)行進(jìn)行內(nèi)存優(yōu)化和垃圾收集器管理,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解下2023-11-11Golang中文件目錄操作的實(shí)現(xiàn)步驟詳解
在Golang中,文件目錄是指計(jì)算機(jī)文件系統(tǒng)中的文件夾或目錄。目錄是用于組織和存儲文件的一種方式,可以包含文件和其他子目錄,本文主要介紹了Golang中文件目錄操作的實(shí)現(xiàn)方法,需要的朋友可以參考下2023-05-05golang之?dāng)?shù)據(jù)驗(yàn)證validator的實(shí)現(xiàn)
這篇文章主要介紹了golang之?dāng)?shù)據(jù)驗(yàn)證validator的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10go語言中嵌套結(jié)構(gòu)體的實(shí)現(xiàn)
在Go語言中,嵌套結(jié)構(gòu)體可定義為一個結(jié)構(gòu)體內(nèi)包含另一個結(jié)構(gòu)體,嵌套可以是值嵌套或指針嵌套,兩者在內(nèi)存分配和修改影響上有顯著區(qū)別,本文就來詳細(xì)的介紹一下,感興趣的可以了解一下2024-09-09golang 實(shí)現(xiàn)tcp server端和client端,并計(jì)算RTT時間操作
這篇文章主要介紹了golang 實(shí)現(xiàn)tcp server端和client端,并計(jì)算RTT時間操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12Golang算法問題之整數(shù)拆分實(shí)現(xiàn)方法分析
這篇文章主要介紹了Golang算法問題之整數(shù)拆分實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了Go語言數(shù)值運(yùn)算與數(shù)組遍歷相關(guān)操作技巧,需要的朋友可以參考下2017-02-02