面试题答案
一键面试可能引发数据竞争的典型场景
- 多个goroutine同时读写共享变量:
- 例如:
package main
import (
"fmt"
)
var sharedVar int
func write() {
for i := 0; i < 1000; i++ {
sharedVar++
}
}
func read() {
for i := 0; i < 1000; i++ {
_ = sharedVar
}
}
func main() {
go write()
go read()
select {}
}
- 在这个例子中,
write
函数对sharedVar
进行写操作,read
函数对sharedVar
进行读操作,两个操作在不同的goroutine中并发执行,没有任何同步机制,这就可能引发数据竞争。
- 多个goroutine同时写共享变量:
- 例如:
package main
import (
"fmt"
)
var sharedVar int
func write1() {
for i := 0; i < 1000; i++ {
sharedVar++
}
}
func write2() {
for i := 0; i < 1000; i++ {
sharedVar++
}
}
func main() {
go write1()
go write2()
select {}
}
- 这里
write1
和write2
两个函数都在不同的goroutine中对sharedVar
进行写操作,同样没有同步机制,容易引发数据竞争。
Go race detector的检测方式
- 插桩:Go race detector通过对代码进行插桩(instrumentation)来实现检测。在编译时,它会在程序的二进制文件中插入额外的代码,这些代码会记录内存访问的相关信息,比如哪个goroutine在什么时候访问了哪个内存地址,是读操作还是写操作等。
- 检测逻辑:当程序运行时,race detector会监控各个goroutine对共享变量的访问情况。如果它发现两个不同的goroutine对同一个共享变量进行了并发的读写操作,并且其中至少有一个是写操作,同时这两个操作之间没有适当的同步机制(如互斥锁等),那么就判定发生了数据竞争,并输出相关的错误信息,包括发生竞争的内存地址、涉及的goroutine以及具体的操作等,方便开发者定位问题。例如运行上述有数据竞争的代码时,race detector会输出类似如下信息:
==================
WARNING: DATA RACE
Write at 0x00c0000140a8 by goroutine 7:
main.write()
/path/to/file.go:8 +0x3e
Previous read at 0x00c0000140a8 by goroutine 8:
main.read()
/path/to/file.go:13 +0x3e
Goroutine 7 (running) created at:
main.main()
/path/to/file.go:18 +0x4e
Goroutine 8 (running) created at:
main.main()
/path/to/file.go:19 +0x6d
==================
这样开发者就能根据这些信息找到发生数据竞争的具体位置和相关的goroutine。