一、简介

最近看到了一些帖子和博客在讨论golang里面对空通道 进行读写操作。本人也比较好奇,就实验了一下,发现和大家的描述都有出入,只好再看源码分析。

二、结论要点

空通道 读写操作会有以下结果:

<- chanchan<-select {case _ <- chan}

三、操作与分析

1、简单源码实验

验证读写休眠:

func main() {
	var c chan struct{}
	go func() {
		fmt.Println("开始接收")
		<-c
		fmt.Println("接收完成")
	}()
	go func() {
		fmt.Println("开始发送")
		c <- struct{}{}
		fmt.Println("发送完成")
	}()
	time.Sleep(5 * time.Second)
}

输出:

开始接收
开始发送

现象:观察到输出上面两行以后会停滞5秒,然后程序退出。

分析可知,当前条件下:

开始接收开始发送接收完成发送完成

2、源码分析

1.17.6

2.1 通道结构

go/src/runtime/chan.gochantype hchan struct{...}

2.2 发送接口

通道的发送接口有3个调用链条:

func chansend1(c *hchan, elem unsafe.Pointer)func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr)func selectnbsend(c *hchan, elem unsafe.Pointer)func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr)func reflect_chansend(c *hchan, elem unsafe.Pointer, nb bool)func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr)
chansend
func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool {
	if c == nil { // channel 为nil时:
		if !block { // 1. 当前以非阻塞模式发送:
			return false // 直接返回false,表示通道已关闭或不可用
		}
      // 2. 当前为阻塞模式:
		gopark(nil, nil, waitReasonChanSendNilChan, traceEvGoStop, 2) // 2.1 协程进入休眠状态
		throw("unreachable") // panic
	}
    ... ...
}

观察代码和注释,可以知道:

func chansend1(c *hchan, elem unsafe.Pointer) {
    chansend(c, elem, true, getcallerpc())
}
func selectnbsend(c *hchan, elem unsafe.Pointer) (selected bool) {
    return chansend(c, elem, false, getcallerpc())
}

参考资料及附录


本文由 qingchuwudi 译制或原创,除非另有声明,在不与原著版权冲突的前提下,本作品采用署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。