select语句channl发送和接收消息专用语句运行期间阻塞的没有case语句当前的groutine阻塞监听goroutineI/O多路复用多个channel可读或可写
正确

1、I/O多路复用

(1)普通多线程(或进程)I/O

创建和维护线程或进程大部分等待状态少部分I/O多路复用

(2)I/O多路复用


每个线程或者进程都先到图中”装置“中注册,然后阻塞,然后只有一个线程在”运输“,当注册的线程或者进程准备好数据后,”装置“会根据注册的信息得到相应的数据。从始至终kernel只会使用图中这个黄黄的线程,无需再对额外的线程或者进程进行管理,提升了效率。

2、select组成结构

常见代码:

	ch1 := make(chan int, 1)
    ch2 := make(chan int, 1)
    ch1 <- 1
    select {
    case e1 := <-ch1:
        //如果ch1通道成功读取数据,则执行该case处理语句
        fmt.Printf("1th case is selected. e1=%v", e1)
    case e2 := <-ch2:
        //如果ch2通道成功读取数据,则执行该case处理语句
        fmt.Printf("2th case is selected. e2=%v", e2)
    default:
        //如果上面case都没有成功,则进入default处理流程
        fmt.Println("default!.")
    }
两部分case语句执行函数
type scase struct {
    c           *hchan         // chan
    elem        unsafe.Pointer // 读或者写的缓冲区地址
    kind        uint16   //case语句的类型,是default、传值写数据(channel <-) 还是  取值读数据(<- channel)
    pc          uintptr // race pc (for race detector / msan)
    releasetime int64
}

hchanchannel的指针等待的协程select中所有的case语句scase结构体数组func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool)
所选scase的索引(是否接收到值
/reflect/value.go/runtime/select.go
func reflect_rselect(cases []runtimeSelect) (int, bool) { 
    //如果cases语句为空,则阻塞当前groutine
    if len(cases) == 0 {
        block()
    }
    //实例化case的结构体
    sel := make([]scase, len(cases))
    order := make([]uint16, 2*len(cases))
    for i := range cases {
        rc := &cases[i]
        switch rc.dir {
        case selectDefault:
            sel[i] = scase{kind: caseDefault}
        case selectSend:
            sel[i] = scase{kind: caseSend, c: rc.ch, elem: rc.val}
        case selectRecv:
            sel[i] = scase{kind: caseRecv, c: rc.ch, elem: rc.val}
        }
        if raceenabled || msanenabled {
            selectsetpc(&sel[i])
        }
    }
    return selectgo(&sel[0], &order[0], len(cases))
}

以上这三个函数的调用栈按顺序如下:

  • func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool)
  • func rselect([]runtimeSelect) (chosen int, recvOK bool) func
  • selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool)
Selectrselect简单的初始化参数func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool)

selectgo函数做了什么

打乱传入的case结构体顺序

锁住其中的所有的channel

遍历所有的channel,查看其是否可读或者可写

groutine阻塞当前所有channel等待队列