在具有竞争条件的程序中添加锁可以解决竞争条件并使竞争检测器保持安静。 Go的种族探测器如何知道锁?

有人指出,“种族探测器只能在真正发生种族状况的情况下才能检测出种族状况”。

考虑以下程序:

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秒钟,另一个goroutine睡眠10秒钟,在大多数情况下,它们不会在同一时间写入a,但是竞速检测器每次都会打印竞态条件警告。为什么?

最佳答案

竞争检测器不会分析源代码,也不知道您在源代码中添加了锁。

种族探测器在运行时工作:



由于这种设计,竞赛检测器只能在实际发生竞赛的情况下以及何时才可以检测出竞赛条件。因此,当您添加适当的锁定/同步时,将不会发生竞争条件(如果不满足if条件),因此不会打印警告。

有关更多详细信息,请参见此博客文章:Introducing the Go Race Detector

而这篇文章:Data Race Detector

编辑您的示例:

您的两个goroutine可能永远不会到达在相同的物理时间写入共享变量a的地步(因为代码运行速度如此之快且睡眠时间相对较长),但是它们在不同的goroutine中并发运行,而没有显式同步(同步点可以是 channel 通信,互斥锁/解锁等)。

竞争条件并不意味着对共享变量的访问确实同时发生(其中之一必须是写操作)。如果对共享变量的访问同时发生(来自多个goroutine),并且没有同步,则也满足竞争条件。种族检测器可以在运行时检测到此情况(由于检测到的内存访问代码)。

允许编译器生成的代码在多个goroutine中使用a变量的缓存实例,运行时仅需保证在达到同步点时“刷新”或处置了缓存实例。有关详细信息,请参见The Go Memory Model

另请注意, time.Sleep() 不保证执行将在指定的持续时间之后立即继续,仅执行将被挂起至少指定的持续时间(因此,执行可在以后的某个时间继续):

10-01 03:35