MST

星途 面试题库

面试题:Go语言中Go race detector如何检测常见的数据竞争场景

请举例说明在Go语言并发编程中,哪些典型场景可能会引发数据竞争,并且阐述Go race detector是怎样检测这些场景的,比如在多个goroutine同时读写共享变量的场景下。
38.4万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

可能引发数据竞争的典型场景

  1. 多个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中并发执行,没有任何同步机制,这就可能引发数据竞争。
  1. 多个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 {}
}
  • 这里 write1write2 两个函数都在不同的goroutine中对 sharedVar 进行写操作,同样没有同步机制,容易引发数据竞争。

Go race detector的检测方式

  1. 插桩:Go race detector通过对代码进行插桩(instrumentation)来实现检测。在编译时,它会在程序的二进制文件中插入额外的代码,这些代码会记录内存访问的相关信息,比如哪个goroutine在什么时候访问了哪个内存地址,是读操作还是写操作等。
  2. 检测逻辑:当程序运行时,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。