Go goroutine与channel示例中为何Greetings done先于Bonjour打印
问题根源:对无缓冲通道的阻塞释放时机理解错误
无缓冲通道的同步规则从来没有要求「发送方必须等接收方把接收动作之后的所有代码执行完才能解除阻塞」,正确的规则非常明确:
当无缓冲通道的发送操作和接收操作在运行时配对,完成值的拷贝传递的瞬间,发送方和接收方会同时从阻塞状态恢复,之后两个协程的后续代码执行顺序完全由Go运行时调度器决定,不存在强制的先后约束。
对应代码的执行流拆解
- main协程创建无缓冲通道
myChannel,启动greetings子协程,随后执行myChannel <- "hi from main"进入阻塞,等待有协程从通道接收值 greetings协程被调度执行,运行到fmt.Println(<-c)时读到main发送的hi from main并打印,第一次通道同步完成:main从发送阻塞恢复,继续向下执行;greetings也继续运行后续代码greetings协程运行到c <- "Bonjour",此时main协程还没执行到通道接收的代码,因此greetings在这里进入阻塞,等待接收方- main协程运行到
fmt.Println(<-myChannel),这行的接收操作和greetings的发送操作成功配对:字符串Bonjour完成从发送方到接收方的拷贝,这一刻两个协程同时解除阻塞,进入可运行状态- 此时两个协程各自的待执行代码分别是:greetings要跑下一行
fmt.Println("Greetings done");main要完成fmt.Println调用,打印刚接收到的Bonjour - Go调度器选择先运行哪个协程是没有固定顺序的:你这次运行时调度器优先执行了greetings协程的剩余代码,因此先打印
Greetings done,等这行执行完调度切回main协程,才会打印Bonjour - 如果你把这段代码反复运行几十次,会发现偶尔也会出现先打印
Bonjour、再打印Greetings done的情况,两种输出都是完全符合Go语言规范的,不存在通道规则被破坏的问题。
- 此时两个协程各自的待执行代码分别是:greetings要跑下一行
预期偏差点
你之前默认greetings协程必须等main协程执行完fmt.Println("Bonjour"),才能从c <- "Bonjour"的阻塞状态退出,这是把通道的同步边界错误放大了。通道的同步保证只覆盖「发送/接收操作本身的配对完成」,不会对两个操作之后的代码执行顺序做任何强制约定。
内容的提问来源于stack exchange,提问作者Poojana V
相关产品推荐
相关产品推荐

