Golang内存模型-并发竞争机制详解

148 阅读2分钟

老生常谈一下,golang中没有线程概念,只有协程。不是搞旅游的那个携程哈。 同时,人家go官方团队也是明确地警告gopher了,不要自做聪明,老老实实地用同步库(sync)、原子库(atomic)以及chan来实现无竞争(data-race-free) go程序就可以了。

听人劝,吃饱饭。

严格按照人家说的上面三种机制,是不用担心竞争的。下面的内容,给同样是“好奇害死人”的技工看的。

Happens-Before机制

golang中的协程同步,只要严格遵守该偏序机制(Partial Order),就能保证竞争安全,或者说无竞争。

Happens-Before主要分为两种具体的实现机制:

  • 顺序先发生(Sequential-Before)
  • 同步先发生(Synchronization-Before)

顺序先发生(Sequential-Before)

顺序先发生,就是说,同一个协程内的一个段代码,执行顺序从上往下,顺序执行,如:

func someMethod() {
    var a = "helloworld"  // 1
    fmt.Println(a)        // 2
}

上述代码,第一行代码一定是在第二行代码之前执行。所以,这两行代码的执行顺序,是可以保证两者无竞争的,或者换句话说,第二行代码fmt.Println(a)的时候,变量a是一定有值的。

func someMethod2() {
    fmt.Println(a)       // 3
}

反之,倘若有另一个方法someMethod2在一个与someMethod不同的协程里执行,那代码3就与代码132之间,就无任何Sequential-Before的关系了。 他们能有的,只可能是Synchronization-Before的关系

同步先发生(Synchronization-Before)

同步先发生,是指两行代码,在不同的协程运行时,因为golang自己的机制、或者因为sync包的功能等原因,导致一个协程的内代码一定先于(或者迟于)另一个协程的某段代码执行,该关系就叫做Synchronized-Before:同步先发生。

golang自带的一些Synchronized-Before机制,如:

  • 如果包p引入了q,则q包的init函数,在包p任何代码被执行前,完成执行
  • 一个go程序里,所有的init函数执行完成后才会执行main.main()函数
  • go someMethod():通过关键字go启动的某个协程的场景中,一定是先执行完go someMethod()这段代码,然后在新协程里才会开始执行someMethod的内容。这个多少有点废话,但是官方就是这么严谨。
  • go关键字启动的新协程,它的执行完成与否,不与任何其他内建机制或事件存在Synchronized-Before关系