V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
flyingpine
V2EX  ›  问与答

对《GO 高级编程》1.5 节面向并发的内存模型的理解问题

  •  
  •   flyingpine · 2018-08-14 13:43:51 +08:00 · 1067 次点击
    这是一个创建于 2293 天前的主题,其中的信息可能已经有所发展或是发生改变。

    go 初学者,在看《 GO 高级编程》,对于下面这句话不是很理解,

    https://chai2010.gitbooks.io/advanced-go-programming-book/content/ch1-basic/ch1-05-mem.html

    “更糟糕的是,因为两个线程之间没有同步事件,setup 线程对 done 的写入操作甚至无法被 main 线程看到,main 函数有可能陷入死循环中。”

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

    在 main 的 for 循环里检查 done 的值,即使某一次检查恰好与 setup 设置 done 相冲突,在下次 for 的检查应该也能发现 done 被设为 true 了啊,为什么会说“ setup 线程对 done 的写入操作甚至无法被 main 线程看到”呢?

    mind3x
        1
    mind3x  
       2018-08-14 15:34:51 +08:00 via Android
    请搜索“内存模型”
    简单的说,现代 CPU 的乱序执行特性使得有可能 done=true 发生在 a 赋值以前。
    flyingpine
        2
    flyingpine  
    OP
       2018-08-14 16:34:56 +08:00
    @mind3x 多谢回复,是的,这个可以解释前一句话:“但是 Go 语言并不保证在 main 函数中观测到的对 done 的写入操作发生在对字符串 a 的写入的操作之后,因此程序很可能打印一个空字符串。” 对于我的问题,刚才又读了很多文档,一个可能的解释是 main thread 的空循环会导致 CPU 不能被调度到 setup,所以 setup 一直得不到执行。
    gamexg
        3
    gamexg  
       2018-08-14 18:12:35 +08:00
    是的,除了你说的还有另外一个可能:

    我对汇编忘的差不多了,
    编译器可能会优化成这样:main 将 done 的值读到了寄存器,之后循环全部是从寄存器读取的,并未去读取内存,所以可能永远不知道 setup 的变更。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1074 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 19:22 · PVG 03:22 · LAX 11:22 · JFK 14:22
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.