内存模型
Go 内存模型描述的是 “在一个 groutine 中对变量进行读操作能够侦测到在其他 gorountine 中对改变量的写操作” 的条件。
happen-before定义
To specify the requirements of reads and writes, we define happens before, a partial order on the execution of memory operations in a Go program. If event e1 happens before event e2, then we say that e2 happens after e1. Also, if e1 does not happen before e2 and does not happen after e2, then we say that e1 and e2 happen concurrently.
这是 Happens Before
的定义,如果 e1 发生在 e2 之前,那么我们就说 e2 发生在 e1 之后,如果 e1 既不在 e2 前,也不在 e2 之后,那我们就说这俩是并发的.
关于channel的happens-before在Go的内存模型中提到了三种情况:
- case1: 对一个channel的发送操作 happens-before 相应channel的接收操作完成
- case2: 关闭一个channel happens-before 从该Channel接收到最后的返回值0
- case3: 不带缓冲的channel的接收操作 happens-before 相应channel的发送操作之前
case1:对一个channel的发送操作 happens-before 相应channel的接收操作完成
测试代码:
import "testing" var c = make(chan int, 10) var a string func f() { a = "hello, world" // (1) c <- 0 // (2) 写操作 发送操作 } func TestMemoryModel(t *testing.T) { go f() <-c // (3) //接收操作 print(a) // (4) }
上面的代码,将保证会打印出 hello world
。有缓冲 channel 写操作发生在接收操作之前。
不带缓冲的channel的接收操作 happens-before 相应channel的发送操作之前
var c1 = make(chan int) var a1 string func f1() { a1 = "hello, world" // (1) <-c1 // (2) 接收操作 } func TestMemoryModel1(t *testing.T) { go f1() c1 <- 0 // (3) 发送操作 print(a1) // (4) }
运行结果:
=== RUN TestMemoryModel1 hello, world--- PASS: TestMemoryModel1 (0.00s) PASS
上面的代码将保证会打印出 hello world
。因为
根据上面的第三条规则(2) happens-before (3),最终可以保证(1) happens-before (4)。
无缓冲 channel
接收操作发生在写操作之前。
再看个例子
var c2= make(chan int, 1) var a2 string func f2() { a2 = "hello, world" // (1) <-c2 // 接收操作 } // 不能保证 打印出 "hello, world" func TestMemoryModel2(t *testing.T) { go f2() c2 <- 0 // (3) print(a2) // (4) 写操作 //var day time.Time //print(day.Format("20060102")) }
上面的代码不能保证打印出 hello world
, 因为输出的channel 是有缓冲的,不能保证接收操作发生在写操作之前,但是能保证写操作发生在接收操作之前。