我尝试go run -race main.go
使用以下代码并获得了WARNING: DATA RACE
,但实际上没有发生任何有害的情况。它有什么危害?
在这段代码中,我尝试同时分配引用,从我的角度来看,它应该是原子的,仅在一条指令中即可。
package main
import (
"context"
"fmt"
"sync"
"time"
)
type my struct {
value string
}
var global *my
func run(v string, ctx context.Context, wg *sync.WaitGroup) {
defer wg.Done()
t := &my{
value: v,
}
l:
for {
select {
case <-ctx.Done():
break l
default:
global = t
}
}
}
func main() {
var a, b int
for i := 0; i < 100; i++ {
ctx, _ := context.WithTimeout(context.Background(), time.Millisecond*10)
wg := sync.WaitGroup{}
wg.Add(2)
go run("1", ctx, &wg)
go run("2", ctx, &wg)
wg.Wait()
if global.value == "1" {
a++
} else {
b++
}
}
fmt.Println(a, b, a + b)
}
我go tool objdump -S programm > 1.txt
并在问题线上获得了一些说明
...
global = t
0x109a1b7 833da22e100000 CMPL $0x0, runtime.writeBarrier(SB)
0x109a1be 750e JNE 0x109a1ce
0x109a1c0 488b442418 MOVQ 0x18(SP), AX
0x109a1c5 48890594740e00 MOVQ AX, main.global(SB)
0x109a1cc ebc1 JMP 0x109a18f
0x109a1ce 488d3d8b740e00 LEAQ main.global(SB), DI
0x109a1d5 488b442418 MOVQ 0x18(SP), AX
0x109a1da e89190fbff CALL runtime.gcWriteBarrier(SB)
0x109a1df ebae JMP 0x109a18f
...
已更新:
我了解到,在go语言中,当两个或多个goroutine并发编写引用变量(在此处Is it safe to read a function pointer concurrently without a lock? 中使用)时,这是未定义的行为。但是怎么可能是错误的,那么在汇编中它可能是一条指令(我对吗?)?为什么在语言规范中未将其定义为线程安全的?例如,在Java中,引用分配是线程安全的。