博客主頁(yè):??看看是李XX還是李歘歘???
??每天不定期分享一些包括但不限于計(jì)算機(jī)基礎(chǔ)、算法、后端開發(fā)相關(guān)的知識(shí)點(diǎn),以及職場(chǎng)小菜雞的生活。??
??點(diǎn)關(guān)注不迷路,總有一些??知識(shí)點(diǎn)??是你想要的???
目錄
什么是線程(并發(fā))安全?
非線程安全原因
map
解決方案
數(shù)組
解決方案
切片
解決方案
Go其他數(shù)據(jù)類型的并發(fā)安全性
先給出結(jié)論:在Go中數(shù)組、切片和map都是非線程安全的。
什么是線程(并發(fā))安全?
線程(并發(fā))安全是指程序在并發(fā)執(zhí)行或者多個(gè)線程同時(shí)操作的情況下,執(zhí)行結(jié)果還是正確的。
非線程安全原因
map
Go語(yǔ)言中的?
map
?在并發(fā)情況下,只讀是線程安全的,同時(shí)讀寫是線程不安全的。同一個(gè)變量在多個(gè)goroutine
中訪問需要保證其安全性。
因?yàn)?code>map變量為指針類型變量,并發(fā)寫時(shí),多個(gè)協(xié)程同時(shí)操作一個(gè)內(nèi)存,類似于多線程操作同一個(gè)資源會(huì)發(fā)生競(jìng)爭(zhēng)關(guān)系,共享資源會(huì)遭到破壞,因此golang
出于安全的考慮,拋出致命錯(cuò)誤:fatal error: concurrent map writes
。
?非并發(fā)安全map(普通的map)
package main
import (
"fmt"
"strconv"
"sync"
)
var m = make(map[string]int)
func get(key string) int {
return m[key]
}
func set(key string, value int) {
m[key] = value
}
func main() {
wg := sync.WaitGroup{}
for i := 0; i < 10; i++ {
wg.Add(1)
go func(n int) {
key := strconv.Itoa(n)
set(key, n)
fmt.Printf("k=:%v,v:=%v\n", key, get(key))
wg.Done()
}(i)
}
wg.Wait()
}
解決方案
(1)在寫操作的時(shí)候增加鎖
package main
import (
"fmt"
"sync"
)
func main() {
var lock sync.Mutex
var maplist map[string]int
maplist = map[string]int{"one": 1, "two": 2}
lock.Lock()
maplist["three"] = 3
lock.Unlock()
fmt.Println(maplist)
}
(2)sync.Map包
package main
import (
"fmt"
"sync"
)
func main() {
m := sync.Map{} //或者 var mm sync.Map
m.Store("a", 1)
m.Store("b", 2)
m.Store("c", 3) //插入數(shù)據(jù)
fmt.Println(m.Load("a")) //讀取數(shù)據(jù)
m.Range(func(key, value interface{}) bool { //遍歷
fmt.Println(key, value)
return true
})
}
數(shù)組
指定索引進(jìn)行讀寫時(shí),數(shù)組是支持并發(fā)讀寫索引區(qū)的數(shù)據(jù)的,但是索引區(qū)的數(shù)據(jù)在并發(fā)時(shí)會(huì)被覆蓋的;
解決方案
1.加鎖
2.控制并發(fā)順序
切片
指定索引進(jìn)行讀寫:是支持并發(fā)讀寫索引區(qū)的數(shù)據(jù)的,但是索引區(qū)的數(shù)據(jù)在并發(fā)時(shí)可能會(huì)被覆蓋的;
發(fā)生切片動(dòng)態(tài)擴(kuò)容:并發(fā)場(chǎng)景下擴(kuò)容可能會(huì)被覆蓋。
切片是對(duì)數(shù)組的抽象,其底層就是數(shù)組,在并發(fā)下寫數(shù)據(jù)到相同的索引位會(huì)被覆蓋,并且切片也有自動(dòng)擴(kuò)容的功能,當(dāng)切片要進(jìn)行擴(kuò)容時(shí),就要替換底層的數(shù)組,在切換底層數(shù)組時(shí),多個(gè)goroutine是同時(shí)運(yùn)行的,哪個(gè)goroutine先運(yùn)行是不確定的,不論哪個(gè)goroutine先寫入內(nèi)存,肯定就有一次寫入會(huì)覆蓋之前的寫入,所以在動(dòng)態(tài)擴(kuò)容時(shí)并發(fā)寫入數(shù)組是不安全的;
解決方案
1.加互斥鎖
2.使用channel串行化操作
3.使用sync.map代替切片(github上著名的iris框架也曾遇到過(guò)切片動(dòng)態(tài)擴(kuò)容導(dǎo)致webscoket連接數(shù)減少的bug,最終采用sync.map解決了該問題,?采用sync.map解決切片并發(fā)安全)
Go其他數(shù)據(jù)類型的并發(fā)安全性
數(shù)據(jù)類型參考:
Go 中所有類型并發(fā)賦值的安全性。
(1)由一條機(jī)器指令完成賦值的類型并發(fā)賦值是安全的,這些類型有:字節(jié)型,布爾型、整型、浮點(diǎn)型、字符型、指針、函數(shù)。
(2)數(shù)組由一個(gè)或多個(gè)元素組成,大部分情況并發(fā)不安全。注意:當(dāng)位寬不大于 64 位且是 2 的整數(shù)次冪(8,16,32,64),那么其并發(fā)賦值是安全的。
(3)struct 或底層是 struct 的類型并發(fā)賦值大部分情況并發(fā)不安全,這些類型有:復(fù)數(shù)、字符串、 數(shù)組、切片、映射、通道、接口。注意:當(dāng) struct 賦值時(shí)退化為單個(gè)字段由一個(gè)機(jī)器指令完成賦值時(shí),并發(fā)賦值又是安全的。這種情況有:
(a)實(shí)部或虛部相同的復(fù)數(shù)的并發(fā)賦值;
(b)等長(zhǎng)字符串的并發(fā)賦值;
(c)同長(zhǎng)度同容量切片的并發(fā)賦值;
(d)同一種具體類型不同值并發(fā)賦給接口。
————————————————
版權(quán)聲明:本文為CSDN博主「戀喵大鯉魚」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/K346K346/article/details/115099353這篇文章寫的很好,感謝博主,但是底層是 struct 的類型的channel是并發(fā)安全的,還有待博主回復(fù)
參考:
(43條消息) 【golang學(xué)習(xí)筆記】Go語(yǔ)言中參數(shù)的傳遞是值傳遞還是引用傳遞_Vivien_oO0的博客-CSDN博客_golang 切片是值傳遞還是引用傳遞
(43條消息) Go并發(fā)安全sync.Map_JIeJaitt的博客-CSDN博客
(43條消息) golang-數(shù)組,切片,map是否線程安全?_golang 切片線程安全_一顆簡(jiǎn)單的心的博客-CSDN博客?(43條消息) Go語(yǔ)言map使用和并發(fā)安全_行走的皮卡丘的博客-CSDN博客_go map并發(fā)安全文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-434773.html
(43條消息) GoLang之切片并發(fā)安全問題_GoGo在努力的博客-CSDN博客_golang 切片線程安全文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-434773.html
到了這里,關(guān)于go中數(shù)組、切片、map是否線程(并發(fā))安全?的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!