背景
并發問題最簡單的解決方案加個鎖,但是,加鎖就會有資源爭用,提高并發能力其中的一個優化方向就是減少鎖的使用。
我在之前的這篇文章《開啟多個協程,并行對struct中的每個元素操作,是否會引起并發問題?》中討論過多協程場景下struct的并發問題。
Go語言中的slice在并發環境下并不是安全的。當多個goroutine同時對一個slice進行修改(如append操作)時,會導致數據競爭(data race),進而引發不可預測的結果,例如數據丟失、覆蓋或程序崩潰。
那么,在什么情況下,可以不使用鎖,也不會導致并發問題呢?
不使用鎖觸發并發案例
案例一:高并發下觸發slice并發競爭問題
package mainimport ("fmt""sync"
)func main() {var wg sync.WaitGroupslice := make([]int, 0)for i := 0; i < 100000; i++ {wg.Add(1)go func(j int) {defer wg.Done()slice = append(slice, j)}(i)}wg.Wait()fmt.Println("理論長度100000,實際長度:", len(slice))
}
通過提高并發數,觸發并發問題暴露。執行的效果,實際的長度小于理論的長度,則證明一定因為并發導致了數據丟失,覆蓋等并發問題。我們可以看一下實際執行的效果。
PS D:\Go\src\tool\concurrent> go run main.go
理論長度1000,實際長度: 88004
PS D:\Go\src\tool\concurrent> go run main.go
理論長度1000,實際長度: 88065
PS D:\Go\src\tool\concurrent> go run main.go
理論長度100000,實際長度: 89078
PS D:\Go\src\tool\concurrent> go run main.go
理論長度100000,實際長度: 88239
權威驗證方法
使用Go內置的競爭檢測器驗證:
go run -race main.go
無論輸出是否正常,該命令會直接告知代碼中數據競爭的位置。
==================
WARNING: DATA RACE
Read at 0x00c000008048 by goroutine 8:main.main.func1()D:/Go/src/tool/concurrent/main.go:16 +0xb9main.main.func2()D:/Go/src/tool/concurrent/main.go:17 +0x41Previous write at 0x00c000008048 by gorouti