go select..case详解


1. go select是一种仅能用于channel发送和接收消息的语句,此语句运行期间是阻塞的;当 select中没有case语句的时候,会阻塞当前goroutine

2. select是go在语言层面提供的IO多路复用机制,专门用于检测多个channel是否准备完毕:可读可写

3. select语句中除default外,每个case操作一个channel,要么读要么写

4. select语句中除default外,各case的执行顺序是完全随机的

5. select中如果没有default语句,则会阻塞等待任一case

6. select语句中读操作要判断是否成功读取,关闭的channel也可以读取

 

实现原理:

Go在实现select时,定义了一个数据结构表示每一个case(包含default, default时一种特殊的case),select执行过程可以类比为一个函数,函数输入case数组,输出选中的case,然后程序流程转到选中的case块。

 

源码包src/runtime/select.go:scase定义了表示case语句的数据结构:

 

type scase struct {
	c           *hchan         // chan,为当前case语句操作的channel指针,一个case语句只能操作一个channel
	elem        unsafe.Pointer // data element, 表示缓冲区地址,从channel中读除的数据或者将要写入channel的数据的存放地址 
	kind        uint16     // 表示case的类型,非为read write default 
	pc          uintptr // race pc (for race detector / msan)
	releasetime int64
}

  

源码包src/runtime/select.go:selectgo()定义了select选择case的函数,伪代码如下:

func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) {
    //1. 锁定scase语句中所有的channel
    //2. 按照随机顺序检测scase中的channel是否ready
    //   2.1 如果case可读,则读取channel中数据,解锁所有的channel,然后返回(case index, true)
    //   2.2 如果case可写,则将数据写入channel,解锁所有的channel,然后返回(case index, false)
    //   2.3 所有case都未ready,则解锁所有的channel,然后返回(default index, false)
    //3. 所有case都未ready,且没有default语句
    //   3.1 将当前协程加入到所有channel的等待队列
    //   3.2 当将协程转入阻塞,等待被唤醒
    //4. 唤醒后返回channel对应的case index
    //   4.1 如果是读操作,解锁所有的channel,然后返回(case index, true)
    //   4.2 如果是写操作,解锁所有的channel,然后返回(case index, false)
}

  

参考文档: https://my.oschina.net/renhc/blog/2253937

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM