英文:
How can Go's race detector be aware of lock?
问题
在具有竞争条件的程序中添加锁可以解决竞争条件,并使竞争检测器保持安静。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秒,另一个睡眠10秒,在大多数情况下它们不会同时写入a
,但是竞争检测器每次都会打印竞争条件警告。为什么会这样?
英文:
Adding lock to a program with race condition can solve the race condition and make the race detector quiet. How can Go's race detector be aware of lock?
Someone points out that "the race detector can only detect race conditions if and when they actually occur".
Consider the following program:
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()
}
One goroutine sleeps for 5 seconds, another sleeps for 10 seconds, they don't write a
at the same time in most cases, but the race detector prints the race condition warning every time. Why?
答案1
得分: 6
竞争检测器不会分析源代码,它不知道你在源代码中添加了锁。
竞争检测器在运行时工作:
当设置了-race
命令行标志时,编译器会插入代码来记录内存访问的时间和方式,而运行时库会监视对共享变量的非同步访问。
由于这种设计,竞争检测器只能在实际发生竞争条件时才能检测到。因此,当你添加适当的锁定/同步时,竞争条件将不会发生(不满足if
条件),因此不会打印任何警告。
请参阅这篇博文以了解更多详细信息:Introducing the Go Race Detector
以及这篇文章:Data Race Detector
针对你的示例进行编辑:
也许你的两个 goroutine 永远不会达到同时写入共享变量 a
的点(因为代码运行得很快,而且睡眠时间相对较长),但它们在不同的 goroutine 中并发运行,没有显式的同步(同步点可以是通道通信、互斥锁的锁定/解锁等)。
竞争条件并不意味着对共享变量的访问一定会发生在同一时间(其中之一必须是写入)。如果在没有同步的情况下,共享变量的访问是并发发生的(来自多个 goroutine),则满足竞争条件。这可以通过竞争检测器在运行时检测到(由于插入的内存访问代码)。
编译器生成的代码允许在多个 goroutine 中使用 a
变量的缓存实例,运行时只需要保证在达到同步点时刷新或丢弃缓存的实例。有关详细信息,请参阅Go 内存模型。
还要注意,time.Sleep()
不能保证执行会在指定的持续时间后立即继续,只能保证执行将至少暂停指定的持续时间(因此执行可能在稍后的时间继续):
> Sleep 暂停当前的 goroutine,至少持续时间为 d。
英文:
The race detector does not analyze the source code, it doesn't know that you added locks in the source code.
The race detector works at runtime:
> 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.
Because of this design, the race detector can only detect race conditions if and when they actually occur. So when you add proper locking / synchronization, the race condition will not happen (the if condition will not be met), and so no warning will be printed.
See this blog post for more details: Introducing the Go Race Detector
And this article: Data Race Detector
Edit for your example:
It may be that your 2 goroutines will never reach the point where they write the shared variable a
at the same physical time (because the code runs so fast and the sleep time is relatively huge), but they run concurrently, in different goroutines, without explicit synchronization (a synchronization point may be channel communication, mutex lock/unlock etc.).
The race condition does not imply that access to a shared variable does happen at the same time (one of which must be a write). The race condition is also met if access to a shared variable happens concurrently (from multiple goroutines), without synchronization. This can be detected at runtime by the race detector (due to the instrumented memory access code).
Code generated by the compiler is allowed to use cached instances of the a
variable in multiple goroutines, the runtime only has to guarantee that the cached instances are "refreshed" or disposed of if a synchronization point is reached. For details, see The Go Memory Model.
Also note that time.Sleep()
does not guarantee that execution will continue right after the specified duration, only that execution will be suspended for at least the specified duration (so the execution may continue at a later time):
> Sleep pauses the current goroutine for at least the duration d.
答案2
得分: 0
数据竞争检测器不进行静态分析。它不知道你的锁。它是经验性的,它只是“注意到”当你在使用锁时,两个线程从未同时写入相同的值(或者一个写入,一个读取)。
英文:
The data race detector does not do static analysis. It is not aware of your lock. It’s empirical, it just notices that when you run your code with the lock, two threads are never writing to the same value at the same time (or one writing, one reading).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论