golang channel 小问题请教

代码1


package main

import (
"fmt"
)

func main() {
ch := make(chan int)
ch <- 1
fmt.Println(<-ch) // 1

}

代码2


package main

import (
"fmt"
)

func main() {
ch := make(chan int, 1)
ch <- 1
fmt.Println(<-ch) // 1

}

问题:
为什么代码1会报死锁的错误,而代码2不会报错?

已邀请:

上面两位说的很清楚了,补充一下Happens Before原则:


对于一个单goroutine来说,读和写操作一定是按照他们语句的顺序被执行。编译器会重排序这些读写语句,这在单goroutine中并不会改变程序的行为。因为这种重排序,一个goroutine观察到的执行顺序可能和其他的goroutine是不一样的,如果一个goroutine执行了 a = 1 b = 2,其他的goroutine可能看到的是b先被复制,然后才是a被赋值。


为了指明读写操作所包含的,我们需要定义Happens Before原则,这是Go程序在内存中操作的准则:



  • 如果事件1先于事件2发生,我们就说事件2后于事件1发生。


  • 如果事件1并没有先于事件2发生,也没有后于事件2发生,那就说事件1和事件2是同时发生的。



在单goroutine中,这个顺序就是程序中表达式的顺序。
如果一个读操作r要想观察到写操作w对于变量v的写操作,就要满足一下的条件:



  • 读操作r不能先于写操作w之前发生。


  • 有其他对于变量v的写操作在写操作w之后并在读操作r之前发生。



为了保证这个读操作r观察到的是写操作w对于变量v的操作结果,为了保证写操作w是读操作r唯一允许的写操作,一定要满足这么两个条件:



  • w先于r之前发生。


  • 任何对于变量v的操作都要在w之前或者是r之后发生。



对于单goroutine来说,由于并没有并发,因此这两条定义是等价的:一个读操作r只能观察到离他最近的写操作w对于变量v的操作结果。当在多个goroutine中共享变量的时候,必须要使用同步事件来建立happens-before的条件以确保读操作r能够观察到它所期望的写操作的结果。


对于变量v的初始化为该类型的0值的行为在内存模型中被认为是一个写操作。


Reads and writes of values larger than a single machine word behave as multiple machine-word-sized operations in an unspecified order.


来自于The Go Memory Model 翻译的一般,见谅

BOBO_leng

赞同来自: lx1259h tz asys0512 逍遥乡

因为代码1的channel没有buffer,而代码2的channel设置了buffer为1。
没有buffer的channel只能通过另一个goroutine去读,否则就阻塞了。


func main() {
messages := make(chan string)
go func() { messages <- "ping" }()
msg := <-messages
fmt.Println(msg)
}

无缓冲的channel, 发送方会一直阻塞到接收方取数据;
所以在ch <- 1 就阻塞住了。

要回复讨论请先登录注册