Go中的以下测试失败:
type A struct {
b bool
}
func TestWG(t *testing.T) {
var wg sync.WaitGroup
a := update(&wg)
wg.Wait()
if !a.b {
t.Errorf("error")
}
}
// Does not work
func update(group *sync.WaitGroup) A {
a := A{
b : false,
}
group.Add(1)
go func() {
a.b = true
group.Done()
}()
return a
}
起初,我认为这可能是由于waitGroup.Done()
中没有屏障而发生的,这可能解释了为什么将update
更改为
// works
func update(group *sync.WaitGroup) A {
a := A{
b : false,
}
group.Add(1)
go func() {
a.b = true
group.Done()
}()
time.Sleep(1*time.Second)
return a
}
工作。但是,将返回类型更改为pointer
也可以使其在中工作
// works
func update(group *sync.WaitGroup) *A {
a := A{
b : false,
}
group.Add(1)
go func() {
a.b = true
group.Done()
}()
return &a
}
有人能告诉我这里发生了什么事吗?
您的第一个示例有一个数据竞赛!
您返回需要读取它的a
,并发goroutine(您刚刚启动(在不同步的情况下写入它。所以输出是未定义的!CCD_ 5也证实了这一点。
添加睡眠时也会发生同样的情况:数据竞争仍然存在(time.Sleep()
是而不是同步工具(,因此结果仍然未定义!go test -race
再次证实了这一点。
当您将返回类型更改为指针时,您只需返回一个指向a
的指针,该指针不涉及读取a
的值,并且启动的goroutine不会修改指针,只修改指向的值。调用方TestWG()
使用waitgroup正确地等待,直到它完成,所以这里不会发生数据竞争。