GoLang切片并發(fā)安全解決方案詳解
1.介紹切片并發(fā)問題
關于切片的,Go語言中的切片原生支持并發(fā)嗎?
2.實踐檢驗真理
實踐是檢驗真理的唯一標準,所以當我們遇到一個不確定的問題,直接寫demo來驗證,因為切片的特點,我們可以分多種情況來驗證
1.不指定索引,動態(tài)擴容并發(fā)向切片添加數(shù)據(jù)
2.指定索引,指定容量并發(fā)向切片添加數(shù)據(jù)
- 不指定索引,動態(tài)擴容并發(fā)向切片添加數(shù)據(jù)
不指定索引,動態(tài)擴容并發(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()
/*第一次運行代碼后,輸出:[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
}- 指定索引,指定容量并發(fā)向切片添加數(shù)據(jù)
指定索引,指定容量并發(fā)向切片添加數(shù)據(jù):
通過結(jié)果我們可以發(fā)現(xiàn)符合我們的預期,長度和容量都是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.回答切片并發(fā)安全問題
我們都知道切片是對數(shù)組的抽象,其底層就是數(shù)組,在并發(fā)下寫數(shù)據(jù)到相同的索引位會被覆蓋,并且切片也有自動擴容的功能,當切片要進行擴容時,就要替換底層的數(shù)組,在切換底層數(shù)組時,多個goroutine是同時運行的,哪個goroutine先運行是不確定的,不論哪個goroutine先寫入內(nèi)存,肯定就有一次寫入會覆蓋之前的寫入,所以在動態(tài)擴容時并發(fā)寫入數(shù)組是不安全的;
所以當別人問你slice支持并發(fā)時,你就可以這樣回答它:
當指定索引使用切片時,切片是支持并發(fā)讀寫索引區(qū)的數(shù)據(jù)的,但是索引區(qū)的數(shù)據(jù)在并發(fā)時會被覆蓋的;當不指定索引切片時,并且切片動態(tài)擴容時,并發(fā)場景下擴容會被覆蓋,所以切片是不支持并發(fā)的~。
4.解決切片并發(fā)安全問題方式
針對上述問題,我們可以多種方法來解決切片并發(fā)安全的問題:
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()
//無論執(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
*/
}
到此這篇關于GoLang切片并發(fā)安全解決方案詳解的文章就介紹到這了,更多相關GoLang切片并發(fā)安全內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Go語言如何利用Mutex保障數(shù)據(jù)讀寫正確
這篇文章主要介紹了互斥鎖的實現(xiàn)機制,以及?Go?標準庫的互斥鎖?Mutex?的基本使用方法,文中的示例代碼講解詳細,需要的小伙伴可以參考一下2023-05-05
golang控制結(jié)構select機制及使用示例詳解
這篇文章主要介紹了golang控制結(jié)構select機制及使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10
Go語言中結(jié)構體方法副本傳參與指針傳參的區(qū)別介紹
這篇文章主要給大家介紹了關于Go語言中結(jié)構體方法副本傳參與指針傳參的區(qū)別的相關資料,文中先對GO語言結(jié)構體方法跟結(jié)構體指針方法的區(qū)別進行了一些簡單的介紹,來幫助大家理解學習,需要的朋友可以參考下。2017-12-12
Go?json自定義Unmarshal避免判斷nil示例詳解
這篇文章主要為大家介紹了Go?json自定義Unmarshal避免判斷nil示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-06-06
go?logger不侵入業(yè)務代碼使用slog替換zap并實現(xiàn)callerSkip詳解
這篇文章主要為大家介紹了go?logger不侵入業(yè)務代碼使用slog替換zap并實現(xiàn)callerSkip詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-09-09

