首頁  >  文章  >  後端開發  >  為什麼在 Go 中附加到切片是線程不安全的?

為什麼在 Go 中附加到切片是線程不安全的?

Susan Sarandon
Susan Sarandon原創
2024-11-09 17:30:02271瀏覽

Why is appending to slices in Go thread-unsafe?

為什麼追加到切片可能是線程不安全的

當多個goroutine 嘗試同時將資料追加到切片時,資料競爭條件可能會發生發生。這是因為 Go 中的切片不是線程安全的,這意味著多個 goroutine 可以同時存取和修改同一個切片頭,這可能會導致資料損壞。

資料爭用說明

考慮以下程式碼:

destSlice := make([]myClass, 0)

var wg sync.WaitGroup
for _, myObject := range sourceSlice {
    wg.Add(1)
    go func(closureMyObject myClass) {
        defer wg.Done()
        var tmpObj myClass
        tmpObj.AttributeName = closureMyObject.AttributeName
        destSlice = append(destSlice, tmpObj)
    }(myObject)
}
wg.Wait()

在此程式碼中,多個 goroutine 同時附加到 destSlice 切片。這可能會導致結果切片中資料遺失或空白,因為 goroutine 可能會交錯其操作並覆蓋彼此的變更。

使用「-race」選項驗證資料爭用

使用「-race」選項執行程式碼將為偵測到的每個資料爭用產生警告。以下輸出說明了所提供程式碼中的資料競爭條件:

==================
WARNING: DATA RACE
Read at 0x00c420074000 by goroutine 6:
  main.main.func1()
      /home/icza/gows/src/play/play.go:20 +0x69

Previous write at 0x00c420074000 by goroutine 5:
  main.main.func1()
      /home/icza/gows/src/play/play.go:20 +0x106

Goroutine 6 (running) created at:
  main.main()
      /home/icza/gows/src/play/play.go:21 +0x1cb

Goroutine 5 (running) created at:
  main.main()
      /home/icza/gows/src/play/play.go:21 +0x1cb
==================

解決方案:使用互斥體進行同步

為了確保執行緒安全的並發追加,您可以使用互斥體來保護destSlice 切片頭。以下修改後的程式碼示範了這一點:

var (
    mu        = &sync.Mutex{}
    destSlice = make([]myClass, 0)
)

var wg sync.WaitGroup
for _, myObject := range sourceSlice {
    wg.Add(1)
    go func(closureMyObject myClass) {
        defer wg.Done()
        var tmpObj myClass
        tmpObj.AttributeName = closureMyObject.AttributeName
        mu.Lock()
        destSlice = append(destSlice, tmpObj)
        mu.Unlock()
    }(myObject)
}
wg.Wait()

透過在每次追加操作之前取得互斥體,可以防止多個goroutine 同時修改切片頭,從而確保資料完整性並消除資料競爭條件。

以上是為什麼在 Go 中附加到切片是線程不安全的?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn