1
kaifang 2020-04-21 19:53:05 +08:00
有区别,
1 进行了运算,取运算后的结果,你加了 1 秒的延时,很明显当时的 num 的值为 123,所以输出是 123,如果去掉延时,则为 789 2 一直指向 num 的地址,从通道里读的时候读的是 789 |
2
bwangel 2020-04-21 20:56:20 +08:00
time.Sleep 不能保证 goroutine 一定比 main 先执行,如果想要让 Goroutine 比 main 先执行的话,可以通过 waitGroup 来同步:
https://gist.github.com/bwangelme/5e71895d40130521b71828cef72adc1f |
3
MoYi123 2020-04-21 21:21:21 +08:00 4
c <- *p + x // 1. 返回 123 等于 tmp:= *p + x ; c<- tmp ,因为 chan 的 size 是 0 所以要等到主进程到 fmt.Println(<-c)时才会运行 c<- tmp
|
4
blip 2020-04-22 03:04:54 +08:00
”By default channels are unbuffered, meaning that they will only accept sends (chan <-) if there is a corresponding receive (<- chan) ready to receive the sent value“
@MoYi123 正解 |
6
davidyanxw OP |
7
davidyanxw OP @kaifang 1 运算了,2 为啥不运算呢?
|
8
kaifang 2020-04-22 15:30:25 +08:00
@davidyanxw #7 2,向通道发送的 num 的地址,此时通道是阻塞的,过一秒后 num 因为赋值 789 地址发生改变,然后从通道里读取 num 值是 789
|
9
blip 2020-04-23 00:11:51 +08:00
@davidyanxw good ask, 我觉得我之前没有抓住对的点,我对 unbuffered channel 的解释其实并没有回答这个问题,这里的重点其实是
1, *p+x 会 create 一个新的 int ( 123 )存放在另一个地址,这个地址存放的值没有改变过 2,*p 会一直等于 num 的地址所存储的值,这个值在代码中被更改为 789 所以才会造成两个 case 结果不同 |
10
davidyanxw OP |
11
guonaihong 2020-04-25 18:13:35 +08:00
这个问题和抛硬币一样。
c <-*p + x,既可能返回 123,也可能返回 789 。这和 thread 切换,先跑了哪个指令有关系(先跑了 num=789 还是后跑了),你可以把 sleep 时间修改小,证明这个现象。 ```go package main import ( "fmt" "time" ) var x = 0 func call() { var num = 123 var p = &num c := make(chan int) go func() { c <- *p + x // 1. 返回 123 // c <- *p // 2. 返回 789 }() time.Sleep(time.Second / 1000) num = 789 readValue := <-c if readValue == 789 { fmt.Printf("hello 789\n") } //fmt.Println(readValue) } func main() { for i := 0; i < 1000000; i++ { call() } } ``` |
12
wsseo 2020-04-26 16:52:17 +08:00
@guonaihong 在 6c6t 的 cpu 上跑了一下,出现了一次 789 。
|
13
davidyanxw OP @guonaihong
能详细解释下 1 这种情况既可能返回 123,也可能返回 789 吗? |
14
wsseo 2020-04-28 14:09:09 +08:00
@davidyanxw 主协程 sleep 1s,基本本可能输出 789 了。
如果主协程 sleep 时间很短,那么 num = 789 可能会比*p + x 先执行 |
15
guonaihong 2020-04-30 18:35:07 +08:00
@wsseo @davidyanxw 这两天有点事就没有回答两位的问题。刚刚做了一些试验,发现了更有意思的地方。
1.如果写 chan 的地方是算术表达式,go 会提前进行预处理(对 c<-*p+0),可能已经换成 c<-123,所有大概率返回 123 2.如果是值,就换按正常逻辑往下走。time.Sleep 之后,两个 go 程开始了竞争,num=789 的执行速度,比唤醒生产者 chan+写数据快,所有大概率返回 789 。 当然上面从数据中总结的规律特别依赖 go 的版本(go 1.13.1),大家也不要太在意。也许哪天人家(Go Core Team)就改了。就当乐一乐,原来 go 还有一些小动作。 ```go package main import ( "fmt" "time" ) var x = 0 func call() { var num = 123 var p = &num c := make(chan int) go func() { //c <- *p + 0 //大概率返回 123 c <- *p // 大概率返回 789 }() time.Sleep(time.Second / 1000) num = 789 readValue := <-c if readValue == 123 { fmt.Printf("hello %d\n", readValue) } } func main() { for i := 0; i < 1000000; i++ { call() } } ``` |