美文网首页
关于Golang并发 "Do not communicate b

关于Golang并发 "Do not communicate b

作者: krystollia | 来源:发表于2019-05-27 15:24 被阅读0次

    "Do not communicate by sharing memory; instead, share memory by communicating" 这句话在讲 Golang 并发的好多文章里看到过,仿佛是一个格言一般。然而对于它的真正含义一直不是很清楚。

    最近看到了如下的代码,才对这句话有所思考和理解。

    摘自 https://golang.org/ref/mem 的一段 错误 的并发代码:

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

    这段代码是典型的 communicate by sharing memory。而官网说到这段错误的代码可能的结局有两种不能接受:

    1. print(a) 打印出来的是空字符串。
    2. main 函数陷入死循环。
      先来解释一下1:Go的内存模型里说到 “happen before” 这样一条原则,也讲了很多种遵循这条原则的操作,比如在一个 goroutine 中如果对一个变量的写w和读r,如果w在代码上位于r的前面,则 w 先于 r 发生。而这里对 a 的写操作 和 对 done 的写操作并不属于 happen before 的范围。也就是说,它们有可能同时发生 甚至 顺序颠倒。所以在 检测到 done 为 true 时,a 还未赋值的情况是有可能发生的。
      再来看2:如果在main函数的一开始或者在 init 函数中通过 runtime.GOMAXPROCS(1) 将并发数设置为1,那么 setup 永远不会执行,main将会陷入死循环。即使 runtime.GOMAXPROCS>1,依然存在 done 一直为false的可能性:如果 setup 所在 goroutine 中对 done 的写入一直不flush到内存(在寄存器或cache),内存中的done就会一直为false。

    官网也讲到了 Go 的各种并发元语是如何满足 happens-before 原则的。思考一下,上述例子在 GOMAXPROCS = 1 时是无法正常执行的,那么那些正确的并发程序在 GOMAXPROCS = 1 时表现如何呢?所谓正确的并发程序,goroutine之间应当是通过并发元语来沟通协作的,那么他们之间必然有某种 happen-before 的约束存在,happen-before 的实质作用其实是将两个goroutine的某一部分串行起来,使用并发原语,也就是为了将需要串行的部分串行起来,这样即使是 GOMAXPROCS = 1,多个goroutine 也是能够正确协作的。

    所以对于 “Do not communicate by sharing memory; instead, share memory by communicating” 这句话,我认为 share memory by communicating 的 communicating 不光是指 channel,Go 的并发元语都是 communicating,Mutex,Once,atomic, channel 等都是 goroutine 之间沟通的正确方式。

    相关文章

      网友评论

          本文标题:关于Golang并发 "Do not communicate b

          本文链接:https://www.haomeiwen.com/subject/jmdhtctx.html