<- 箭头操作符

Channel 是 Go 中的一个核心类型,你可以把它看成一个管道,通过它并发核心单元就可以发送或者接收数据进行通讯。

Channel 的操作符是箭头 <- ,例如:

ch <- v // 发送值v到Channel ch中
v := <- ch // 从Channel ch中接收数据,并将数据赋值给v

注:
箭头的指向就是数据的流向

就像 map 和 slice 数据类型一样, channel必须先创建再使用:

ch := make(chan int)
ch := make(chan int)

Channel类型

Channel类型的定义格式如下:

//ChannelType - 通道类型
//ElementType - 元素类型
ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .
ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .

它包括三种类型的定义。可选的 <- 代表channel的方向。如果没有指定方向,那么Channel就是双向的,既可以接收数据,也可以发送数据。

chan T // 可以接收和发送类型为 T 的数据
chan <- float64 // 只可以用来发送 float64 类型的数据
<- chan int // 只可以用来接收 int 类型的数据

<-总是优先和最左边的类型结合。(The <- operator associates with the leftmost chan possible)

chan<- chan int    // 等价 chan<- (chan int)
chan<- <-chan int  // 等价 chan<- (<-chan int)
<-chan <-chan int  // 等价 <-chan (<-chan int)
chan (<-chan int)

使用make初始化Channel,并且可以设置容量:

make(chan int, 100)
make(chan int, 100)

容量(capacity)代表Channel容纳的最多的元素的数量,代表Channel的缓存的大小
如果没有设置容量,或者容量设置为0, 说明Channel没有缓存,只有sender和receiver都准备好了后它们的通讯(communication)才会发生(Blocking)。

如果设置了缓存,就有可能不发生阻塞, 只有buffer满了后 send才会阻塞, 而只有缓存空了后receive才会阻塞。一个nil channel不会通信。

可以通过内建的close方法可以关闭Channel

你可以在多个goroutine 从/往 一个channel 中 receive/send 数据, 不必考虑额外的同步措施。

Channel可以作为一个先入先出(FIFO)的队列,接收的数据和发送的数据的顺序是一致的。

channel的 receive支持 multi-valued assignment,如

v, ok := <-ch
v, ok := <-ch

它可以用来检查Channel是否已经被关闭了。

什么是channel?

chan是Go中的一种派生类型,不同的线程可以通过 channel 进行数据交换

channel分为有缓冲区无缓冲区两种。

无缓冲区 chan

如果当前线程向无缓冲区chan发送一条消息,但是这个chan没有就绪的接收线程时,当前协程就会被阻塞,直到出现一个接收者协程就绪,并从chan中接收这条消息。

有缓冲区 chan

缓冲区被填满后,尝试向chan发送消息的协程将会被阻塞。当缓冲区内消息数量为0时,尝试从chan中读取消息的协程将被阻塞。

channel的底层实现

channel是基于环形队列实现的。

type hchan struct {
  qcount uint          // 当前队列中剩余元素个数
  dataqsiz uint        // 环形队列长度(可以存放的元素个数)
  buf unsafe.Pointer   // 环形队列指针
  elemsize uint16      // 每个元素的⼤⼩
  closed uint32        // 标识关闭状态
  elemtype *_type      // 元素类型
  sendx uint           // 队列下标,指示元素写入时存放到队列中的位置 x
  recvx uint           // 队列下标,指示元素从队列的该位置读出  
  recvq waitq          // 等待读消息的协程队列
  sendq waitq          // 等待写消息的协程队列
  lock mutex           // 互斥锁,chan不允许并发读写
} 

环形队列(buf)

由buf指针指向环形队列,所有进入到chan的数据都将会存入环形队列中。

等待队列(recvq / sendq)

读消息的协程等待队列(recvq):

如果该chan缓冲区为空,或者该chan没有缓冲区,那么当前的读消息协程将被阻塞。被阻塞的读消息协程会变放入读消息的协程等待队列。

写消息的协程等待队列(sendq):

如果该chan缓冲区已满,或者该chan没有缓冲区,那么当前的写消息协程将被阻塞。被阻塞的写消息协程会变放入写消息的协程等待队列。


来源: