这个单例实例实现有竞争条件吗?

问题描述

有人告诉我 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
}

解决方法

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

如果从多个 goroutine 调用 arr,您的示例会发生数据竞争。这个简单的例子触发了竞态检测器:

GetMemCache()

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

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

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

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

    return memCacheInstance
}

另请注意,如果您“立即”初始化变量(在声明时或在包 var ( memCacheInstance *MemCache memCacheOnce sync.Once ) func GetMemCache() *MemCache { memCacheOnce.Do(func() { memCacheInstance = &MemCache{ data: make([]string,} }) return memCacheInstance } 函数中),则不需要同步(因为包初始化在单个 goroutine 中运行):

init()

在这种情况下,您也可以选择导出变量,然后就不需要 var memCacheInstance = &MemCache{ data: make([]string,} func GetMemCache() *MemCache { return memCacheInstance }