Go 的竞争检测器如何感知锁定?

How can Go's race detector be aware of lock?

给有竞争条件的程序加锁可以解决竞争条件并使竞争检测器安静下来。 Go 的竞争检测器如何感知锁?

有人指出"the race detector can only detect race conditions if and when they actually occur"。

考虑以下程序:

package main

import (
    "sync"
    "time"
)

func main() {
    var a int
    var wg sync.WaitGroup
    workers := 2
    wg.Add(workers)
    for i := 1; i <= workers; i++ {
        go func(sleep int) {
            time.Sleep(time.Duration(sleep) * time.Second)
            a = 1
            wg.Done()
        }(i * 5)
    }
    wg.Wait()
}

一个 goroutine 休眠 5 秒,另一个休眠 10 秒,在大多数情况下它们不会同时写入 a,但竞争检测器每次都会打印竞争条件警告。为什么?

race detector不分析源码,它不知道你在源码中加了锁。

竞争检测器在 运行 时间工作:

When the -race command-line flag is set, the compiler instruments all memory accesses with code that records when and how the memory was accessed, while the runtime library watches for unsynchronized accesses to shared variables.

由于这种设计,竞争检测器只能检测竞争条件 ifwhen 它们实际发生。因此,当您添加适当的锁定/同步时,竞争条件将不会发生(if 条件将不会满足),因此不会打印警告。

有关详细信息,请参阅此博客 post:Introducing the Go Race Detector

以及这篇文章:Data Race Detector

为您的示例编辑:

可能你的2个goroutines永远不会达到他们在同一物理时间写入共享变量a的地步(因为代码运行s太快而且睡眠时间是比较庞大),但是它们运行 并发,在不同的goroutines中,没有显式同步(一个同步点可能是channel通信,mutex lock/unlock 等)。

竞争条件并不意味着对共享变量的访问确实会同时发生(其中一个必须是写入)。如果在没有同步的情况下并发(来自多个 goroutines)访问共享变量,也会满足竞争条件。这可以通过竞争检测器在 运行 时间检测到(由于检测内存访问代码)。

允许编译器生成的代码在多个goroutines中使用cached变量的a实例,运行time只需要保证如果达到同步点,则缓存的实例 "refreshed" 或被丢弃。有关详细信息,请参阅 The Go Memory Model

另请注意,time.Sleep() 不保证执行会在指定的持续时间后立即继续,只是执行会暂停 至少 指定的持续时间(因此执行可能会在 之后 时间继续执行):

Sleep pauses the current goroutine for at least the duration d.

数据竞争检测器不进行静态分析。它不知道你的锁。这是经验性的,它只是 注意到 当你 运行 你的带有锁的代码时,两个线程永远不会同时写入相同的值(或者一个写入,一个读取).