GoLang切片并發(fā)安全解決方案詳解
1.介紹切片并發(fā)問題
關(guān)于切片的,Go語言中的切片原生支持并發(fā)嗎?
2.實(shí)踐檢驗(yàn)真理
實(shí)踐是檢驗(yàn)真理的唯一標(biāo)準(zhǔn),所以當(dāng)我們遇到一個不確定的問題,直接寫demo來驗(yàn)證,因?yàn)榍衅奶攸c(diǎn),我們可以分多種情況來驗(yàn)證
1.不指定索引,動態(tài)擴(kuò)容并發(fā)向切片添加數(shù)據(jù)
2.指定索引,指定容量并發(fā)向切片添加數(shù)據(jù)
- 不指定索引,動態(tài)擴(kuò)容并發(fā)向切片添加數(shù)據(jù)
不指定索引,動態(tài)擴(kuò)容并發(fā)向切片添加數(shù)據(jù):
通過打印數(shù)據(jù)發(fā)現(xiàn)每次len與cap的結(jié)果都不一致
func concurrentAppendSliceNotForceIndex() { sl := make([]int, 0) wg := sync.WaitGroup{} for index := 0; index < 100; index++ { k := index wg.Add(1) go func(num int) { sl = append(sl, num) wg.Done() }(k) } wg.Wait() fmt.Println(sl) fmt.Printf("final len(sl)=%d cap(sl)=%d\n", len(sl), cap(sl)) } func main() { concurrentAppendSliceNotForceIndex() /*第一次運(yùn)行代碼后,輸出:[2 0 1 5 6 7 8 9 10 4 17 11 12 13 14 15 16 21 18 19 20 23 22 24 25 26 39 27 28 29 30 31 35 55 54 56 57 58 59 60 61 62 64 63 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 86 91 92 93 94 96 95 97 98 99] final len(sl)=74 cap(sl)=128*/ //第二次運(yùn)行代碼后,輸出:省略切片元素輸出... final len(sl)=81 cap(sl)=128 //第二次運(yùn)行代碼后,輸出:省略切片元素輸出... final len(sl)=77 cap(sl)=128 }
- 指定索引,指定容量并發(fā)向切片添加數(shù)據(jù)
指定索引,指定容量并發(fā)向切片添加數(shù)據(jù):
通過結(jié)果我們可以發(fā)現(xiàn)符合我們的預(yù)期,長度和容量都是100
func concurrentAppendSliceForceIndex() { sl := make([]int, 100) wg := sync.WaitGroup{} for index := 0; index < 100; index++ { k := index wg.Add(1) go func(num int) { sl[num] = num wg.Done() }(k) } wg.Wait() fmt.Println(sl) fmt.Printf("final len(sl)=%d cap(sl)=%d\n", len(sl), cap(sl)) } func main() { concurrentAppendSliceForceIndex() /*第一次運(yùn)行代碼后,輸出:[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 7 9 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99] final len(sl)=100 cap(sl)=100*/ /*第一次運(yùn)行代碼后,輸出:[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 7 9 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99] final len(sl)=100 cap(sl)=100*/ /*第一次運(yùn)行代碼后,輸出:[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 7 9 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99] final len(sl)=100 cap(sl)=100*/ }
3.回答切片并發(fā)安全問題
我們都知道切片是對數(shù)組的抽象,其底層就是數(shù)組,在并發(fā)下寫數(shù)據(jù)到相同的索引位會被覆蓋,并且切片也有自動擴(kuò)容的功能,當(dāng)切片要進(jìn)行擴(kuò)容時,就要替換底層的數(shù)組,在切換底層數(shù)組時,多個goroutine是同時運(yùn)行的,哪個goroutine先運(yùn)行是不確定的,不論哪個goroutine先寫入內(nèi)存,肯定就有一次寫入會覆蓋之前的寫入,所以在動態(tài)擴(kuò)容時并發(fā)寫入數(shù)組是不安全的;
所以當(dāng)別人問你slice支持并發(fā)時,你就可以這樣回答它:
當(dāng)指定索引使用切片時,切片是支持并發(fā)讀寫索引區(qū)的數(shù)據(jù)的,但是索引區(qū)的數(shù)據(jù)在并發(fā)時會被覆蓋的;當(dāng)不指定索引切片時,并且切片動態(tài)擴(kuò)容時,并發(fā)場景下擴(kuò)容會被覆蓋,所以切片是不支持并發(fā)的~。
4.解決切片并發(fā)安全問題方式
針對上述問題,我們可以多種方法來解決切片并發(fā)安全的問題:
1.加互斥鎖
2.使用channel串行化操作
3.使用sync.map代替切片
5.附
設(shè)置為1的的時候,runtime.GOMAXPROCS(1)
package main import ( "fmt" "runtime" "sync" ) func concurrentAppendSliceNotForceIndex() { sl := make([]int, 0) wg := sync.WaitGroup{} for index := 0; index < 100; index++ { k := index wg.Add(1) go func(num int) { sl = append(sl, num) wg.Done() }(k) } wg.Wait() fmt.Println(sl) fmt.Printf("final len(sl)=%d cap(sl)=%d\n", len(sl), cap(sl)) } func main() { runtime.GOMAXPROCS(1) concurrentAppendSliceNotForceIndex() /* [99 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 5 5 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98] final len(sl)=100 cap(sl)=128 */ /* [13 0 1 2 3 4 5 6 7 8 9 10 11 12 99 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 5 5 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98] final len(sl)=100 cap(sl)=128 */ /* [10 0 1 2 3 4 5 6 7 8 9 99 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 5 5 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98] final len(sl)=100 cap(sl)=128 */ }
package main import ( "fmt" "runtime" "sync" ) var wg sync.WaitGroup var sl []int func add() { for index := 0; index < 100; index++ { sl = append(sl, index) } wg.Done() } func main() { runtime.GOMAXPROCS(1) wg.Add(1) go add() wg.Wait() //無論執(zhí)行多少次都輸出一下結(jié)果 fmt.Println(sl) fmt.Printf("final len(sl)=%d cap(sl)=%d\n", len(sl), cap(sl)) /* [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 6 3 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99] final len(sl)=100 cap(sl)=128 */ }
package main import ( "fmt" "runtime" "sync" ) var wg sync.WaitGroup var sl []int func add() { for index := 0; index < 50; index++ { sl = append(sl, index) } wg.Done() } func main() { runtime.GOMAXPROCS(1) wg.Add(2) go add() go add() wg.Wait() //無論執(zhí)行多少次都輸出一下結(jié)果 fmt.Println(sl) fmt.Printf("final len(sl)=%d cap(sl)=%d\n", len(sl), cap(sl)) /* [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49] final len(sl)=100 cap(sl)=128 */ }
不限數(shù)量:
package main import ( "fmt" "sync" ) var wg sync.WaitGroup var sl []int func add() { for index := 0; index < 50; index++ { sl = append(sl, index) } wg.Done() } func main() { wg.Add(2) go add() go add() wg.Wait() fmt.Println(sl) fmt.Printf("final len(sl)=%d cap(sl)=%d\n", len(sl), cap(sl)) /* [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49] final len(sl)=82 cap(sl)=128 */ }
加鎖
package main import ( "fmt" "sync" ) var wg sync.WaitGroup var sl []int var lock sync.Mutex func add() { for index := 0; index < 50; index++ { lock.Lock() sl = append(sl, index) lock.Unlock() } wg.Done() } func main() { wg.Add(2) go add() go add() wg.Wait() fmt.Println(sl) fmt.Printf("final len(sl)=%d cap(sl)=%d\n", len(sl), cap(sl)) /* [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49] final len(sl)=100 cap(sl)=128 */ /* [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 33 34 35 3 6 37 38 39 40 41 42 43 44 45 46 47 48 49] final len(sl)=100 cap(sl)=128 */ }
到此這篇關(guān)于GoLang切片并發(fā)安全解決方案詳解的文章就介紹到這了,更多相關(guān)GoLang切片并發(fā)安全內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
go語言使用pipe讀取子進(jìn)程標(biāo)準(zhǔn)輸出的方法
這篇文章主要介紹了go語言使用pipe讀取子進(jìn)程標(biāo)準(zhǔn)輸出的方法,實(shí)例分析了Go語言針對進(jìn)程操作的技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-03-03快速掌握Go 語言 HTTP 標(biāo)準(zhǔn)庫的實(shí)現(xiàn)方法
基于HTTP構(gòu)建的服務(wù)標(biāo)準(zhǔn)模型包括兩個端,客戶端(Client)和服務(wù)端(Server),這篇文章主要介紹了Go 語言HTTP標(biāo)準(zhǔn)庫的實(shí)現(xiàn)方法,需要的朋友可以參考下2022-07-07Go實(shí)現(xiàn)替換(覆蓋)文件某一行內(nèi)容的示例代碼
本文主要介紹了Go實(shí)現(xiàn)替換(覆蓋)文件某一行內(nèi)容的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07golang微服務(wù)框架kratos實(shí)現(xiàn)Socket.IO服務(wù)的方法
本文主要介紹了golang微服務(wù)框架kratos實(shí)現(xiàn)Socket.IO服務(wù)的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06一個Pod調(diào)度失敗后重新觸發(fā)調(diào)度的所有情況分析
這篇文章主要為大家介紹了一個Pod調(diào)度失敗后重新觸發(fā)調(diào)度的所有情況分析詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04golang中的select關(guān)鍵字用法總結(jié)
這篇文章主要介紹了golang中的select關(guān)鍵字用法總結(jié),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-06-06