我在 Go 中使用
sync.Map
的 LoadOrStore
方法。我试图了解是否可能存在竞争条件导致对价值创造函数进行多次评估。我试图了解这是否准确以及如何正确处理。
package main
import (
"fmt"
"sync"
)
type HashIDMap struct {
m sync.Map
}
func (hm *HashIDMap) GetOrCreate(key, value string, createFunc func(string, string)) string {
actual, loaded := hm.m.LoadOrStore(key, value)
if !loaded {
// If the value was not loaded, it means we stored it and need to create the database entry
createFunc(key, value)
}
return actual.(string)
}
func createDatabaseEntry(key, value string) {
// Simulate database entry creation
fmt.Printf("Creating database entry for key: %s with value: %s\n", key, value)
}
func main() {
hm := &HashIDMap{}
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
id := hm.GetOrCreate("hash_1", "id_1", createDatabaseEntry)
fmt.Println("Goroutine 1 got ID:", id)
}()
go func() {
defer wg.Done()
id := hm.GetOrCreate("hash_1", "id_1", createDatabaseEntry)
fmt.Println("Goroutine 2 got ID:", id)
}()
wg.Wait()
}
我读过一些建议,由于竞争条件,
createFunc
实际上可能被执行多次。
根据我的理解,这正是
LoadOrStore
想要解决的问题,但现在我不再有信心竞争条件是否真的可能。
同一键是否可以调用两次
createDatabaseEntry
?
sync.Map
的文档指出(强调我的):
用Go内存模型的术语来说,Map安排写操作“同步在”任何观察写效果的读操作之前,其中读操作和写操作定义如下。 Load、LoadAndDelete、LoadOrStore、Swap、CompareAndSwap、CompareAndDelete是读操作; Delete、LoadAndDelete、Store、Swap是写操作; LoadOrStore 当返回loaded设置为false时是一个写操作
因此,正如预期的那样,对返回
LoadOrStore
(写操作)的 false
的调用保证发生在返回 LoadOrStore
(读操作)的 true
之前。