前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go语言中常见100问题-#83 Not enabling the race flag

Go语言中常见100问题-#83 Not enabling the race flag

作者头像
数据小冰
发布2022-08-15 15:30:41
2530
发布2022-08-15 15:30:41
举报
文章被收录于专栏:数据小冰
未启用数据竞争检测

Go语言中常见100问题-#58 Not understanding race problems中,举了一个存在数据竞争的例子,例子中有两个goroutine同时访问同一个变量,并且至少有一个对变量存在写操作。除了知道数据存在竞争外,我们还应该知道,在Go语言官方工具库中有一个工具可以检测数据竞争。一个常见的问题是忘记了这个工具的重要性并且没有启用它。本文将深入研究数据竞争检测器捕获警告内容的含义、如何开启竞争检测以及如何剔除不想进行竞争检测的文件。

Go语言中的竞争检测工具不是静态分析工具,也就是说该工具不是在编译期间生效,相反,它是一种查找程序运行时是否存在数据竞争的工具。要启用数据竞争检查,必须在编译或运行测试时添加-race选项,例如像下面这样:

代码语言:javascript
复制
$ go test -race ./...

添加-race编译选项后,编译器将在代码中插入检查代码(instrumentation指令),该指令会跟踪所有内存访问并记录它们发生的时间和方式。在程序运行时,竞争检测指令将监视数据竞争情况。但有一点需要了解,启动数据竞争检测在运行时是有开销的:

  • 内存使用量可能会增加5-10倍
  • 执行时间可能会增加2-20倍

由于存在上述开销,建议仅在本地测试或CI期间启动竞争检查。在生产环境中,应该关闭竞争检查(或者只在金丝雀版本中使用)。此外,还有一点我们需要注意,无论执行上下文如何,Go数据竞争检查对同时执行的goroutine的数量有一个严格限制,这个数量值是8128。超过这个阈值,竞争检查器将停止工作。

如果检测到存在数据竞争,Go程序会产生警告。例如,下面的程序中存在数据竞争问题,因为变量i可以同时被多个goroutine进行读取和写入操作。

代码语言:javascript
复制
package main

import (
        "fmt"
)

func main() {
        i := 0
        go func() { i++ }()
        fmt.Println(i)
}

使用 -race 参数运行上述程序将产生如下警告信息:

代码语言:javascript
复制
==================
WARNING: DATA RACE
Write at 0x00c000026078 by goroutine 7:
  main.main.func1()
      /tmp/app/main.go:9 +0x4e

Previous read at 0x00c000026078 by main goroutine:
  main.main()
      /tmp/app/main.go:10 +0x88

Goroutine 7 (running) created at:
  main.main()
      /tmp/app/main.go:9 +0x7a
==================

为了输出的警告信息方便我们理解和排查,告警信息会记录如下信息:

  • 是哪些goroutine导致了数据竞争:这里是main goroutine和goroutine 7.
  • 数据竞争在代码中产生的位置:这里是在第9行和第10行.
  • 产生数据竞争的goroutine是在什么时候创建的:goroutine 7是在main()函数中创建的.

「NOTE: 在内部实现上,竞争检测器采用了向量时钟(vector clocks)技术,这是一种用于确定事件偏序的数据结构(也用于分布式系统如数据库)。每个goroutine被创建都会创建一个向量时钟,然后instrumentation指令在每次内存访问和同步事件时更新向量时钟,通过比较向量时钟来判断是否存在数据竞争。」

需要注意,竞争检测器不会产生错误的上报,即不会出现实际没有数据竞争但检测器上报存在数据竞争的情况。因此,如果我们收到了警告信息,便可知道程序代码中包含有数据竞争。但是会存在这样的情况,代码实际上存在数据竞争,但是检测器没有检查出来,因为检测器依赖于代码运行,如果某些存在竞争情况没有运行到,便检测不出来。进行测试的时候注意两件事情:一是,竞争检测效果依赖于测试情况,所以对并发代码在数据竞争方面应该进行彻底运行测试,测试的越充分越能够检测出数据竞争;二是,考虑到有些数据竞争不容易检测出来,如果有一个测试来检测数据竞争,一个选择是将这个待测逻辑放在一个循环中,像下面的程序。这样,可以增加捕获可能的数据竞争机会。

代码语言:javascript
复制
func TestDataRace(t *testing.T) {
        for i := 0; i < 100; i++ {
                // Actual logic
        }
}

在对程序测试的时候,如果特定测试文件会导致数据竞争产生,我们可以使用 !race 编译标签将其从竞争检测中排除。

代码语言:javascript
复制
//go:build !race

package main

import (
        "testing"
)

func TestFoo(t *testing.T) {
        // ...
}

func TestBar(t *testing.T) {
        // ...
}

上述文件只会在当关闭竞争检测(即不带 -race选项时)才会构建。否则,整个文件不会被构建,也就不会执行文件里面的测试项。

总结:我们应该牢记,如果不是强制性的,强烈建议使用-race为带有并发的应用程序进行测试。通过-race选项启动数据竞争检测器。该检测器会检查我们的代码并捕获潜在的数据竞争。注意,启用检测器后会对内存和性能产生重要影响,因此它必须在特定的条件下使用,例如本地测试或CI环境。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-07-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据小冰 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 未启用数据竞争检测
相关产品与服务
检测工具
域名服务检测工具(Detection Tools)提供了全面的智能化域名诊断,包括Whois、DNS生效等特性检测,同时提供SSL证书相关特性检测,保障您的域名和网站健康。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档