如何理解Go内存模型中的“错误同步”示例

k2fxgqgv  于 2023-06-19  发布在  Go
关注(0)|答案(2)|浏览(129)

我刚刚开始学习golang,当阅读Go内存模型时,我得到了一个问题来理解它所说的“另一个不正确的习惯用法是忙碌着等待一个值”

var a string
var done bool
func setup() {
    a = "hello, world"
    done = true
}
func main() {
    go setup()
    for !done {
    }
    print(a)
}

它说:
“更糟糕的是,无法保证要完成的写操作会被main观察到,因为在两个线程之间没有同步事件。main中的循环不能保证完成。”
我知道在setup()中写入'a'和'done'的顺序是不确定的,我的问题是:为什么main不能保证看到要完成的写入?
谢谢你

7kjnsjlb

7kjnsjlb1#

package main

var a string

var done bool

func setup() {
    a = "hello, world"
    done = true
}
func main() {
    go setup()
    for !done {
    }
    println(a)
}

你有两个goroutine,main和setup。例如,假设它们在具有本地存储器高速缓存的单独CPU上的单独线程上运行。假设main和setup都将done变量读入本地CPU内存缓存。setupgoroutine在其本地内存缓存中进行更新,该缓存执行延迟写入。由于在独立的主goroutine和设置goroutine之间没有cache同步事件,所以不能保证cache一致性,不能保证主内存和两个CPU内存缓存将同步。不同的硬件对此的处理方式不同。在Go中,只能保证最小公分母。
参见Cache coherence

z9smfwbn

z9smfwbn2#

go内存模型是一个 axios 化内存模型,它是根据happens-before关系定义的。
所以如果A发生在B之前,那么B应该在A之前看到A和一切。
在Golang中,由于sequence-before规则,单个Goroutine中的所有操作都会创建这样的happen-before边。所以Goroutine应该总是能够看到自己的变化。
但是如果在不同的Goroutine之间共享状态,如果没有happens-before边,事情就会变得有问题。
2当对共享位置的内存操作中至少有一个是写操作时,则这些操作是冲突的。当你有两个冲突的内存操作是并发的,所以它们之间没有happens-before边,那么你就有了一个数据竞争。
一旦你有了数据竞赛,奇怪的问题就会发生。所以典型的原子性、可见性和重新排序问题。
在您的示例中,在'a'和'done'上存在数据竞争。

func setup() {
    a = "hello, world" (1)
    done = true (2)
}
func main() {
    go setup()
    for !done { (3)
    }
    println(a) (4)
}

例如,设置功能中的2个存储可以被重新排序。要解决此问题,您需要使用原子更新'done'。
在这种情况下,由于sequenced-before,在(1)和(2)之间有一个happens-before边。也在(3)和(4)之间。在(2)和(3)之间,存在由于同步之前而导致的之前发生边缘。因为happens-before是传递的,所以在(1)和(4)之间有一个happens-before边。因此,你得到了保证,当你读到'done = true'时,你会看到'a = hello world'。

相关问题