霸王餐 (下)
先来看看这个不认识的 0x00c00006a068
我们把题目补充一些打印输出,
在 counter := 0
的下一行补充一句 fmt.Println("&counter", &counter)
补充后的代码如下:
package main
import (
"fmt"
"sync"
)
func main() {
counter := 0
fmt.Println("&counter", &counter)
wg := sync.WaitGroup{}
for i := 0; i < 10000; i++{
wg.Add(1)
go func() {
counter++
wg.Done()
}()
}
wg.Wait()
fmt.Println(counter)
}
&
叫做取址符,也就是获取内存地址的符号.
&counter
也就表示获取变量 counter
的内存地址.
然后我们运行起来看看:
go run -race main.go
输出如下:
&counter 0x00c00006a068
==================
WARNING: DATA RACE
Read at 0x00c00006a068 by goroutine 8:
main.main.func1()
E:/xiaobai/main.go:15 +0x3f
Previous write at 0x00c00006a068 by goroutine 7:
main.main.func1()
E:/xiaobai/main.go:15 +0x55
Goroutine 8 (running) created at:
main.main()
E:/xiaobai/main.go:14 +0x181
Goroutine 7 (finished) created at:
main.main()
E:/xiaobai/main.go:14 +0x181
==================
9998
Found 1 data race(s)
exit status 66
通过 &counter
对变量 counter
进行取址操作,我们可以看到 0x00c00006a068
其实是变量 counter
的内存地址.
然后在 7 号协程和 8 号协程之间发生了竞争读取和写入,以及发生竞争的代码行数信息等.
总而言之:当很多协程并发执行共享同一内存地址时发生的竞争现象,是一种常见的并发问题,通常称之为 “竞态条件”.
也可以称之为 “非线程安全”, 当然在这里叫做 “非协程安全” 更合适.
在 Go 1.1 后提供了竞态条件检测器,简单的使用方式就是在运行命令中添加 -race
参数指令.
新技能 Get, 那么以后有事没事都可以用竞态检测器检查一下我写的代码咯 —— 小白心想.
嘿嘿,终于能吃上 “霸王餐” 了.