1.概述
| 字段 | 类型 | 含义 |
|---|---|---|
| raceenabled | bool | 定义在runtime包中。它用于启用数据竞争检查功能,让编译器和运行时监视器能检测到并报告潜在的危险行为 |
2.函数
2.1 racereadpc
调用此函数的函数指针,用于定位当前函数在代码中的位置。
FuncPCABIInternal,用于获取chansend函数的指针,表示这个函数的PC值是在Go函数调用协议内部的PC值。这种类型的函数的调用方式与其他函数略有不同,需要专门处理。
在下边的代码中在很多源码场景都出现过,其中有channel的源码,下边看代码,揪出内部实现。
具体作用是要进行并发竞争检测,当race detector被启用时,将记录并获取chansend函数的调用信息和调用位置。
具体来说,该代码用到了Go语言中的race detector,用来检测并发程序中的数据竞争问题。
其中,变量raceenabled用于判断race detector是否被启用。若被启用,则调用racereadpc函数,该函数的作用是向race detector注册读取操作,并记录chansend函数的调用信息和调用位置。这样,race detector就可以通过跟踪这些信息,识别并报告潜在的并发竞争问题。
总之,这段代码用于提高程序对并发竞争问题的检测能力,帮助开发者消除潜在的并发安全隐患。
if raceenabled {
racereadpc(c.raceaddr(), callerpc,abi.FuncPCABIInternal(chansend))
}
2.3 racenotify
racenotify函数是一个race detector的辅助函数,它用于在发送和接收操作之前或之后通知race detector。
例如,如果我们在执行发送操作之前调用racenotify,则race detector将知道程序正尝试进行发送操作,并可以在需要时捕获任何可能的竞争。
3.例子
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
counter := 0
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
counter++
wg.Done()
}()
}
wg.Wait()
fmt.Printf("Counter: %d\n", counter)
}
在上边代码中,我们定义了一个计数器 counter,然后启动了1000个协程,每个协程都将计数器的值增加1,。在主函数中,我们等待所有协程完成,然后打印计数器的值。
我们可以使用Racer Detector竞争检测器来检测是否存在数据竞争问题。在编译和运行代码时,我们可以使用-race选项开启Racer Detector。将上述代码保存为test.go,并在终端执行以下命令:
go run -race test.go
如果输出中有以下一行:
WARNING: DATA RACE
就说明存在数据竞争问题。然后,Racer Detector会告诉我们具体在哪里发生了数据竞争:
WARNING: DATA RACE
Read at 0x00c0000cc00a by goroutine 8:
main.main.func1()
/path/to/test.go:12 +0x41
Previous write at 0x00c0000cc00a by goroutine 7:
main.main.func1()
/path/to/test.go:12 +0x6b
Goroutine 8 (running) created at:
main.main()
/path/to/test.go:19 +0x86
Goroutine 7 (running) created at:
main.main()
/path/to/test.go:19 +0x86
这里告诉我们,都有两个goroutine(7和8)争夺了变量counter的访问权限。因为这个例子造成数据竞争的原因非常显而易见,你应该已经明白了如何使用Racer Detector去检测数据竞争问题。