目录

Channel

性质

  • 向已经关闭的通道发送数据,会引发panic

  • close已经关闭的channel或值为nilchannel,会引发panic

  • 从已关闭接收数据,返回已缓冲数据或零值

  • 无论收发,nil通道都会阻塞

type hchan struct {
    buf      unsafe.Pointer // 环形队列指针
    elemtype *_type         // 元素类型
    elemsize uint16         // 每个元素的大小
    
    dataqsiz uint           // 环形队列长度
    qcount   uint           // 剩余位置数

    sendx    uint           // 队列写入位置
    recvx    uint           // 队列读出位置
    
    recvq    waitq          // 等待读消息的 goroutine 队列
    sendq    waitq          // 等待写消息的 goroutine 队列
    
    closed   uint32         // 标识关闭状态
    lock mutex              // 互斥锁,chan不允许并发读写
}
环形队列示意图

挂载的Goroutine示意图

环形队列的剩余存储空间大小,决定了Goroutine读写Channel时,是否发生堵塞。

当剩余空间发生变化时,将会唤醒相应的阻塞状态的Goroutine

由于Channel内部带了锁机制,所以它同时仅允许被一个goroutine读写。

向Channel中写入数据示意图

从Channel中读取数据

关闭Channel

以下两种情况,会报panic

  • 关闭值为nil的channel

  • 关闭已经被关闭的channel

关闭channel时:

  • 会把recvq中的G全部唤醒,本该写入G的数据位置为nil

  • sendq中的G全部会panic,即向已经关闭的channel写数据会panic

Select

type scase struct {
    c           *hchan                         // 当前case语句所操作的channel指针
    kind        uint16                         // 类型,分为读channel、写channel和default
    elem        unsafe.Pointer // 读出/写入channel的数据存放地址(缓冲区地址)
}

range for channel

// range channel 底层实现
for {
    value_temp, ok = <-range
    if !ok{
        break
    }
    value = value_temp
}
  • range channel时,没有数据,会发生阻塞

将通道限定为单向的

一般用这个限定通道方向来获得更严谨的逻辑,并且这个限定是不可逆的。

var wg sync.WaitGroup
wg.Add(2)

c := make(chan int)
var send chan<- int = c // 限定为只写
var recv <-chan int = c // 限定为只读

close(send)                              // 只能close发送端