// exp3 用来测试访问一个已经关闭的且里面还有值未取出的 chanel 会发生什么事? // 结果是先取出 chanel 里面的值,以后返回零值 func exp3(){ sigCh:= make(chan string) ch:=make (chan int,2) ch <- 3 ch <- 4 close(ch) go func (){ for i:=0;i<6;i++ { fmt.Println("b goroutine recieve: " ,<-ch) } sigCh <-"over" }() <-sigCh }
控制台结果如图:segmentfault
// exp4 用来测试往一个已经关闭的 chanel 写入数据会发生什么事? // 结果是发生 panic :send on closed channel func exp4 (){ sigCh:= make(chan string) ch:=make (chan int,2) ch <- 3 ch <- 4 close(ch) go func (){ ch <- 6 sigCh <-"over" }() <-sigCh }
控制台结果如图:数组
// exp5 关闭已经关闭的 chanel 会发生什么事? // 结果是发生 panic : close of closed channel func exp5 (){ sigCh:= make(chan string) ch:=make (chan int,2) ch <- 3 ch <- 4 close(ch) go func (){ close(ch) sigCh <-"over" }() <-sigCh }
没有共享内存(除了 hchan 对象)“不经过共享内存来通讯,而是经过通讯来共享内存”,而怎么通讯呢?其实就是 copies。缓存
goroutine 是一种用户级线程,由 Go 运行时系统来建立和管理,而不是操做系统。相较于操做系统的线程更加轻量级。一个 OS thread 对应对个 goroutine。这即是 Go 语言的 M:N 调度模型。安全
MGP 模型,其中 P 为调度所须要的上下文资源,拥有运行队列。数据结构
当 G1 发送数据给已经满了的 chanel 时,会经过调用 gopark() 方法呼叫调度器将当前 goroutine(即 G1)设置为 waiting 状态。并发
而后断开 G1 和 M 的联系函数
调度下一条可运行的 goroutine 测试
被阻塞的 发送方、接收方 goroutine 分别保存在 hchan 结构体的 sendq 、recvq 字段中。而且是封装为一个个 sudog 结构体对象保存的。ui
G1 建立一个 sudog 结构体对象,而后将 sudog 放入 hchan 类型对象中的 sendq 队列上。spa
接下来,当 G2 取走 chanel 中的数据,chanel 缓存有空余,G1 再也不阻塞,可是是谁调用调度器将 G1 唤醒的呢?
如上图,是 G2 ,经过调用 goready(G1) 方法呼叫调度器将 G1 唤醒。因此说“经过通讯来共享内存”
当 chanel 有数据不为空了,G1 并非:
而是有一种更聪明的办法,直接写到等待接受队列中的 G2
关于无缓存 chanel ,若是接收方先阻塞,则发送方“直接写入”数据到接受方的栈结构中。
若是发送方先阻塞,则接收方直接从发送方的 sudog 结构中获取数据。
引用文章出处:
https://speakerdeck.com/kavya...
https://segmentfault.com/a/11...