博客主頁(yè):🏆看看是李XX還是李歘歘?🏆
成都創(chuàng)新互聯(lián)公司是一家專業(yè)從事網(wǎng)站設(shè)計(jì)、成都網(wǎng)站設(shè)計(jì)的網(wǎng)絡(luò)公司。作為專業(yè)網(wǎng)站建設(shè)公司,成都創(chuàng)新互聯(lián)公司依托的技術(shù)實(shí)力、以及多年的網(wǎng)站運(yùn)營(yíng)經(jīng)驗(yàn),為您提供專業(yè)的成都網(wǎng)站建設(shè)、營(yíng)銷型網(wǎng)站及網(wǎng)站設(shè)計(jì)開(kāi)發(fā)服務(wù)!🌺每天不定期分享一些包括但不限于計(jì)算機(jī)基礎(chǔ)、算法、后端開(kāi)發(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é)果還是正確的。
非線程安全原因 mapGo語(yǔ)言中的?
map
在并發(fā)情況下,只讀是線程安全的,同時(shí)讀寫是線程不安全的。同一個(gè)變量在多個(gè)goroutine
中訪問(wèn)需要保證其安全性。
因?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解決了該問(wèn)題,?采用sync.map解決切片并發(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)聲明:本文為博主「戀喵大鯉魚」的原創(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的博客-博客_golang 切片是值傳遞還是引用傳遞
(43條消息) Go并發(fā)安全sync.Map_JIeJaitt的博客-博客
(43條消息) golang-數(shù)組,切片,map是否線程安全?_golang 切片線程安全_一顆簡(jiǎn)單的心的博客-博客?(43條消息) Go語(yǔ)言map使用和并發(fā)安全_行走的皮卡丘的博客-博客_go map并發(fā)安全
(43條消息) GoLang之切片并發(fā)安全問(wèn)題_GoGo在努力的博客-博客_golang 切片線程安全
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購(gòu),新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧