Go 语言,深入了解 select 实现原理
select 是 GO 语言中用来提供 IO 复用的机制,它可以检测多个 chan 是否 ready(可读/可写)。
老规矩,我们先来答几道题试试水。
答题环节
下面程序输出什么?
答案:
select 中的 case 执行顺序是随机的,如果某个 case 中的 channel 已经 ready,那么就会执行相应的语句并退出 select 流程,如果所有 case 中的 channel 都未 ready,那么就会执行 default 中的语句然后退出 select 流程。
由于启动的协程和 select 语句并不能保证执行的顺序,所以也有可能 select 执行时协程还未向 channel 中写入数据,所以 select 直接执行 default 语句并退出。因此,次程序有可能产生三种输出:
下面程序输出什么?
答案:
和第一题一样,select 会随机检测各 case 语句中 channel 是否 ready,如果有 case 中 channel 已经 ready 则执行相应的 case 语句后退出 select 流程,如果所有的 channel 都未 ready 且没有 default 的话,则会阻塞等待各个 channel。因此上述程序会一直阻塞。
下面程序输出什么?
答案:
select 会随机检测各 case 语句中 channel 是否 ready,注意已关闭的 channel 也是可读的,所以上述程序中 select 不会阻塞,具体执行哪个 case 语句具是随机的。
下面程序输出什么?
答案:对于空的 select 语句,程序会被阻塞,确切的说是当前协程被阻塞,同时 Go 自带死锁检测机制,当发现当前协程再也没有机会被唤醒时,则会发生 panic。所以上述程序会 panic。
实现原理
Go 实现 select 时,定义了一个数据结构表示每个 case 语句(包含 defaut),select 执行过程可以类比成一个函数,函数输入 case 数组,输出选中的 case,然后程序流程转到选中的 case 块。
源码包 src/runtime/select.go
定义了表示 case 语句的数据结构:
c : 表示当前 case 语句所操作的 channel 指针
elem :表示缓冲区地址
归纳总结
select 语句中除 default 外,每个 case 操作一个 channel,要么读要么写
select 语句中除 default 外,各 case 执行顺序是随机的
select 语句中如果没有 default 语句,则会阻塞等待任一 case
select 语句中读操作要判断是否成功读取,关闭的 channel 也可以读取
版权声明: 本文为 InfoQ 作者【微客鸟窝】的原创文章。
原文链接:【http://xie.infoq.cn/article/1dc898e15b41a2722a25467b3】。文章转载请联系作者。
评论