使用 WaitGroup 从不同的 go 例程写入堆栈变量是否安全?

Is it safe to write to on-stack variables from different go routine blocking current one with WaitGroup?

任务执行器种类繁多,性质各异,有的仅支持非阻塞调用。所以,我在想,是否需要使用 mutex/channel 来安全地将任务结果传递给调用 go-routine,或者是否足够简单 WaitGroup?

为了问题的简单性和特异性,一个使用非常简单的任务执行器启动函数的例子直接作为go例程:

func TestRace(t *testing.T) {
    var wg sync.WaitGroup

    a, b := 1, 2

    wg.Add(1)

    // this func would be passed to real executor
    go func() {
        a, b = a+1, b+1
        wg.Done()
    }()

    wg.Wait()

    assert.Equal(t, a, 2)
    assert.Equal(t, b, 3)
}

在我的机器上,使用 -race 选项执行上述测试没有失败。然而,这就足够保证了吗?如果 go-routine 在不同的 CPU 核心上执行,或者在 CPU 核心块(AMD CCX)上,或者在多插槽设置中的不同 CPU 上执行怎么办?

所以,问题是,我可以使用 WaitGroup 为非阻塞执行程序提供同步(块和 return 值)吗?

JimB 也许应该提供这个作为答案,但我将从 , starting with :

复制它

The WaitGroup here is to ensure that a, b = a+1, b+1 has executed, so there's no reason to assume it hasn't.

[和]

[T]he guarantees you have are laid out by the go memory model, which is well documented [here]. [Specifically, the combination of wg.Done() and wg.Wait() in the example suffices to guarantee non-racy access to the two variables a and b.]

只要这个问题存在,最好也复制 :

As @JimB noted, if a value is shared between goroutines, it cannot be stack-allocated, so the question is moot (see ). WaitGroup works correctly.

闭包变量是堆分配的这一事实是一个实现细节:将来可能不是这样。但是 sync.WaitGroup 保证 在未来仍然是正确的,即使一些聪明的未来 Go 编译器能够将这些变量保存在一些堆栈上。

("Which stack?" 完全是另一个问题,但是假设未来聪明的 Go 编译器会回答这个问题。WaitGroup 和内存模型提供了规则。)