先來看看這個不認識的 0x00c00006a068
我們把題目補充一些列印輸出,
在 counter := 0
的下一行補充一句 fmt.Println("&counter", &counter)
補充後的程式碼如下:
package main
import (
"fmt"
"sync"
)
func main() {
counter := 0
fmt.Println("&counter", &counter)
wg := sync.WaitGroup{}
for i := 0; i < 10000; i++{
wg.Add(1)
go func() {
counter++
wg.Done()
}()
}
wg.Wait()
fmt.Println(counter)
}
&
叫做取址符, 也就是獲取記憶體地址的符號.
&counter
也就表示獲取變數 counter
的記憶體地址.
然後我們執行起來看看:
go run -race main.go
輸出如下:
&counter 0x00c00006a068
==================
WARNING: DATA RACE
Read at 0x00c00006a068 by goroutine 8:
main.main.func1()
E:/xiaobai/main.go:15 +0x3f
Previous write at 0x00c00006a068 by goroutine 7:
main.main.func1()
E:/xiaobai/main.go:15 +0x55
Goroutine 8 (running) created at:
main.main()
E:/xiaobai/main.go:14 +0x181
Goroutine 7 (finished) created at:
main.main()
E:/xiaobai/main.go:14 +0x181
==================
9998
Found 1 data race(s)
exit status 66
通過 &counter
對變數 counter
進行取址操作, 我們可以看到 0x00c00006a068
其實是變數 counter
的記憶體地址.
然後在 7 號協程和 8 號協程之間發生了競爭讀取和寫入, 以及發生競爭的程式碼行數資訊等.
總而言之: 當很多協程併發執行共享同一記憶體地址時發生的競爭現象, 是一種常見的併發問題, 通常稱之為 “競態條件”.
也可以稱之為 “非執行緒安全”, 當然在這裡叫做 “非協程安全” 更合適.
在 Go 1.1 後提供了競態條件檢測器, 簡單的使用方式就是在執行命令中新增 -race
引數指令.
新技能 Get, 那麼以後有事沒事都可以用競態檢測器檢查一下我寫的程式碼咯 —— 小白心想.
嘿嘿, 終於能吃上 “霸王餐” 了.
本作品採用《CC 協議》,轉載必須註明作者和本文連結