Go Threads and Raft

Go Memory Model: https://golang.org/ref/mem

Happens Before

Hanpens Before,为了深入研究同步问题而提出的概念:

  • 关于一个变量 v 的读语句 r 与写语句 wr 可以得到 w 的值需要满足以下条件:
    1. r does not happene before w;
    2. There is not other w' that happens before r and after w;
  • 一个读语句 v 能够准确地捕获到某个特定的 w 语句的值,需要满足以下条件:
    1. w happends before r;
    2. Any other w' to v either hanppends before w or after r;

Golang Lifetime

Initialization, 一个 golang 程序的初始化:

  • 对于库 p 引用库 q 的情况,p 的初始化函数开始在 q 的初始化函数结束之前;
  • 在函数 main.main 执行前,所有函数的 init 函数都应该执行完成了;

Goroutine destruction:

  • Goroutine 的销毁不会被保证发生在任何事件之前(not guaranteed to happen before any event)。怎么理解这个 happened before 呢?比如下面这个例子:

  • 1
    2
    3
    4
    5
    
    var a string
    func hello() {
    	go func() { a = "hello" }()
    	print(a)
    }
    

    其中,a 的赋值语句发生在一个单独的 goroutine 中,它之后没有任何同步语句,所以它有可能不会被任何事件使用,激进一点的编译器会直接拒绝编译。

Communication

Golang 中线程同步的方法。

Channel

Example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
var c = make(chan int)
var a string

func f() {
	a = "hello, world"
	<-c
}

func main() {
	go f()
	c <- 0
	print(a)
}

这段代码一定会输出 hello world,因为根据上面的理论:

  • a = "hello world" happends before <- c happends before f() happends before print(a);

Locks

Example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
var l sync.Mutex
var a string

func f() {
	a = "hello, world"
	l.Unlock()
}

func main() {
	l.Lock()
	go f()
	l.Lock()
	print(a)
}

上面这段代码一定会输出 hello world,因为根据理论:

  • a = "hello world" happends before 第一个 l.Unlock() happens before 第二个 l.Lock() happens before print(a);

Once

Example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
var a string
var once sync.Once

func setup() {
	a = "hello, world"
}

func doprint() {
	once.Do(setup)
	print(a)
}

func twoprint() {
	go doprint()
	go doprint()
}

上面这个代码中保证了 setup 这个函数只能被调用一次,并且 print 一定会输出 hello world 函数,这是因为 once.Do 保证了每次调用都会在第一调用结束之后。