```go
package main
import (
"fmt"
"time"
)
func main() {
var num = 10
var p = &num
c := make(chan int)
go func() {
time.Sleep(time.Second)
c <- *p //-----> 11
//c <- num //----->10
}()
time.Sleep(2 * time.Second)
num++
fmt.Println(<-c)
fmt.Println(p)
return
}
```
为什么 *p 和num的结果不一样
规范里面对Send statements的说明是:
Both the channel and the value expression are evaluated before communication begins
问题的关键在于`communication begins`发生在什么时候?
这里有4个时间点:
A:`num++`
B:`<-c`
C: `c <- *p`
D: `*p`这个表达式求值
`communication begins`可能指的时间点是B或C,就是说D可能发生在goroutine里面`time.Sleep(time.Second)`之后,B或C之前的任意时刻
根据Go memory model,有几个happen before是可以确定的:
A在B之前,B在C之前
那么就有下面两种情况:
1. 如果D发生在B之前,由于A也在B之前,那么不能确定A和D谁先在前
2. 如果D发生在C之前,由于A也在C之前,那么也不能确定A和D的顺序
在A和D的执行顺序不确定的情况下,同一程序每次执行的结果都可能不同,更不要指望把D里面的`*p`换成`num`后两者执行结果每次都相同
实际应用中也不会有这样用Go用的不彻底的代码:用channel传递一个全局变量,却没有在从channel里面获取到这个变量之后再修改
`Do not communicate by sharing memory; instead, share memory by communicating.`
#23
更多评论