Go 武林外傳 - 霸王餐 (下)

taadis發表於2020-03-20

先來看看這個不認識的 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 協議》,轉載必須註明作者和本文連結

覺得還不錯點個讚唄!

相關文章