並發存取 Go 中的共享資料可能是潛在錯誤的來源,例如資料爭用。當對資料結構的存取是並發的,即多個 goroutine 可以同時存取它時,確保資料的讀寫同步以避免不一致是至關重要的。
考慮以下Go struct 元資料:
type Metadata struct { mu sync.RWMutex // ? key bool }
如我們所見,元資料結構體包含一個bool 類型的欄位key 和另一個sync.RWMutex 類型的欄位mu,它是讀寫鎖的實作。
如果我們建立一個 Metadata 實例並允許多個 goroutine 同時讀寫它的字段,我們可能會遇到資料競爭。當多個 Goroutines 並發存取相同的資料並且至少其中一個正在執行寫入操作時,就會發生資料競爭。
以下程式碼示範了在沒有明確鎖定的情況下對Metadata 結構體的並發讀寫存取:
func concurrentStruct() { m := new(Metadata) for i := 0; i < 100000; i++ { go func(metadata *Metadata) { for { readValue := metadata.key if readValue { metadata.key = false } } }(m) go func(metadata *Metadata) { for { metadata.key = true } }(m) } select {} }
在這段程式碼中,我們建立了一個並發讀取和寫入關鍵字段的goroutine。我們使用 select 語句來阻止主 goroutine,從而允許並發 goroutine 運行。使用 go run -race 指令執行程序,我們會收到一則警告,指出出現了 DATA RACE。
但是,程式繼續運行,沒有崩潰。這是因為Go運行時有內建的並發檢查,但它不能保證安全執行。在這種情況下,資料爭用可能會導致未定義的行為和不正確的結果。
為了防止並發讀取和寫入結構時出現資料爭用,我們需要使用正確的方法鎖定機制。一種方法是使用互斥鎖,如以下程式碼所示:
func concurrentStructWithMuLock() { m := new(Metadata) go func(metadata *Metadata) { for { metadata.mu.Lock() readValue := metadata.key if readValue { metadata.key = false } metadata.mu.Unlock() } }(m) go func(metadata *Metadata) { for { metadata.mu.Lock() metadata.key = true metadata.mu.Unlock() } }(m) select {} }
在此程式碼中,我們向元資料結構添加了讀寫鎖,並使用 mu.Lock() 和 mu。 Unlock() 同步對關鍵字段的存取。使用 go run -race 執行程式將不再產生任何警告,表示不存在資料爭用。
以上是並發讀寫Go結構體時如何防止資料爭用?的詳細內容。更多資訊請關注PHP中文網其他相關文章!