網站首頁 編程語言 正文
1.介紹切片并發問題
關于切片的,Go語言中的切片原生支持并發嗎?
2.實踐檢驗真理
實踐是檢驗真理的唯一標準,所以當我們遇到一個不確定的問題,直接寫demo來驗證,因為切片的特點,我們可以分多種情況來驗證
1.不指定索引,動態擴容并發向切片添加數據
2.指定索引,指定容量并發向切片添加數據
- 不指定索引,動態擴容并發向切片添加數據
不指定索引,動態擴容并發向切片添加數據:
通過打印數據發現每次len與cap的結果都不一致
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() /*第一次運行代碼后,輸出:[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*/ //第二次運行代碼后,輸出:省略切片元素輸出... final len(sl)=81 cap(sl)=128 //第二次運行代碼后,輸出:省略切片元素輸出... final len(sl)=77 cap(sl)=128 }
- 指定索引,指定容量并發向切片添加數據
指定索引,指定容量并發向切片添加數據:
通過結果我們可以發現符合我們的預期,長度和容量都是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() /*第一次運行代碼后,輸出:[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*/ /*第一次運行代碼后,輸出:[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*/ /*第一次運行代碼后,輸出:[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.回答切片并發安全問題
我們都知道切片是對數組的抽象,其底層就是數組,在并發下寫數據到相同的索引位會被覆蓋,并且切片也有自動擴容的功能,當切片要進行擴容時,就要替換底層的數組,在切換底層數組時,多個goroutine是同時運行的,哪個goroutine先運行是不確定的,不論哪個goroutine先寫入內存,肯定就有一次寫入會覆蓋之前的寫入,所以在動態擴容時并發寫入數組是不安全的;
所以當別人問你slice支持并發時,你就可以這樣回答它:
當指定索引使用切片時,切片是支持并發讀寫索引區的數據的,但是索引區的數據在并發時會被覆蓋的;當不指定索引切片時,并且切片動態擴容時,并發場景下擴容會被覆蓋,所以切片是不支持并發的~。
4.解決切片并發安全問題方式
針對上述問題,我們可以多種方法來解決切片并發安全的問題:
1.加互斥鎖
2.使用channel串行化操作
3.使用sync.map代替切片
5.附
設置為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() //無論執行多少次都輸出一下結果 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() //無論執行多少次都輸出一下結果 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 */ }
不限數量:
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 */ }
原文鏈接:https://blog.csdn.net/qq_53267860/article/details/126820348
相關推薦
- 2022-04-17 C語言中用棧+隊列實現隊列中的元素逆置_C 語言
- 2023-02-03 C++中的HTTP協議問題_C 語言
- 2022-11-20 Golang交叉編譯之跨平臺編譯使用詳解_Golang
- 2022-12-04 Android?MQTT與WebSocket協議詳細講解_Android
- 2022-10-19 R語言初學者的一些常見報錯指南_R語言
- 2022-07-11 spring boot + shiro 無需redis自定義token生成
- 2022-09-03 golang架構設計開閉原則手寫實現_Golang
- 2023-03-20 c#判斷代碼是否執行超時的幾種方式總結_C#教程
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支