0
点赞
收藏
分享

微信扫一扫

079-竞争检测


大多数时候,我们对并发的直觉都是错误的,特别是在多核处理器上更为明显。这也是高并发程序难写的原因。

人类想了种种办法来降低程序员犯错误的机率,发明了很多方法。甚至在有些语言里,干脆就不支持多线程了,比如基于 NodeJs 开发的服务器。

但在 Golang 里我们无法逃避这种并发竞争。

1. 竞争例子

下面这个例子开启了 100 个 goroutine,读写全局变量 a.

// demo01.go
package main

import (
"fmt"
"sync"
)

var a int

func run() {
a++
}

func main() {
var wg sync.WaitGroup
num := 100
wg.Add(num)
for i := 0; i < num; i++ {
go func() {
run()
wg.Done()
}()
}
wg.Wait()
fmt.Printf("a = %d\n", a)
}


079-竞争检测_golang


图1 运行结果


上面的程序反复运行了几次,发现有几次结果不太一样,我们期望的结果应该是 100。

原因就在于 a++ 这一行语句不是原子执行的。

2. 竞争检测

golang 提供了竞争检测工具,用来辅助我们发现程序种出现的数据竞争。只要在命令后面加上 -race 选项就可以了:

$ go test -race mypkg    // 测试包
$ go run -race mysrc.go // 编译和运行程序
$ go build -race mycmd // 构建程序
$ go install -race mypkg // 安装程序

在我们这个例子中,你可以使用下面的方法:

$ go run -race demo01.go

或者你也可以使用

$ go build -race demo01.go
$ ./demo01


079-竞争检测_高并发_02


图2 使用竞争检漏工具


竞争检测器集成在go工具链中。当使用了-race作为命令行参数后,编译器会插桩代码,使得所有代码在访问内存时,会记录访问时间和方法。同时运行时库会观察对共享变量的未同步访问。

3. 使用互斥锁解决竞争问题

// demo02.go
package main

import (
"fmt"
"sync"
)

var a int
var mu sync.Mutex

func run() {
mu.Lock()
defer mu.Unlock()
a++
}

func main() {
var wg sync.WaitGroup
num := 100
wg.Add(num)
for i := 0; i < num; i++ {
go func() {
run()
wg.Done()
}()
}
wg.Wait()
fmt.Printf("a = %d\n", a)
}

接下来再运行 ​​go run -race demo02.go​​,就没有问题了,而且每次结果都是 100.


079-竞争检测_i++_03


图3 运行结果


4. 总结

  • 学会使用竞争检测工具


举报

相关推荐

0 条评论