0

有人告诉我memCacheInstance有比赛条件,但go run -race不能说。

代码:

type MemCache struct {
    data []string
}

var memCacheInstance *MemCache
var memCacheCreateMutex sync.Mutex

func GetMemCache() *MemCache {
    if memCacheInstance == nil {
        memCacheCreateMutex.Lock()
        defer memCacheCreateMutex.Unlock()

        if memCacheInstance == nil {
            memCacheInstance = &MemCache{
                data: make([]string, 0),
            }
        }
    }
    return memCacheInstance
}
4

1 回答 1

5

go 比赛检测器不会检测到每场比赛,但当它检测到时,它总是一个积极的案例。您必须编写模拟出轨行为的代码。

GetMemCache()如果从多个 goroutine 调用,则您的示例存在数据竞争。这个简单的例子触发了比赛检测器:

func main() {
    go GetMemCache()
    GetMemCache()
}

运行它go run -race .,输出为:

==================
WARNING: DATA RACE
Read at 0x000000526ac0 by goroutine 6:
  main.GetMemCache()
      /home/icza/gows/src/play/play.go:13 +0x64

Previous write at 0x000000526ac0 by main goroutine:
  main.GetMemCache()
      /home/icza/gows/src/play/play.go:18 +0x17e
  main.main()
      /home/icza/gows/src/play/play.go:28 +0x49

Goroutine 6 (running) created at:
  main.main()
      /home/icza/gows/src/play/play.go:27 +0x44
==================
Found 1 data race(s)
exit status 66

它有一个竞争,因为memCacheInstance变量的第一次读取没有锁定,没有同步。必须同步对变量的所有并发访问,其中至少有一个访问是写入。

一个简单的修复方法是删除未同步的读取:

func GetMemCache() *MemCache {
    memCacheCreateMutex.Lock()
    defer memCacheCreateMutex.Unlock()

    if memCacheInstance == nil {
        memCacheInstance = &MemCache{
            data: make([]string, 0),
        }
    }

    return memCacheInstance
}

另请注意,要仅以并发安全的方式执行某些代码,有sync.Once. 你可以这样使用它:

var (
    memCacheInstance *MemCache
    memCacheOnce     sync.Once
)

func GetMemCache() *MemCache {
    memCacheOnce.Do(func() {
        memCacheInstance = &MemCache{
            data: make([]string, 0),
        }
    })

    return memCacheInstance
}

另请注意,如果您“立即”初始化变量(在声明时或在包init()函数中),则不需要同步(因为包初始化在单个 goroutine 中运行):

var memCacheInstance = &MemCache{
    data: make([]string, 0),
}

func GetMemCache() *MemCache {
    return memCacheInstance
}

在这种情况下,您也可以选择导出变量,然后就不需要GetMemCache().

于 2021-02-23T12:52:27.097 回答