channel 名为通道,主要用于goroutine之间的通信;
他有收发两方,发送者可以向通道发送数据,如果通道里面的数据还没有被取走,那么发送方将阻塞;接受者也是同样,可以从通道里拿去数据,如果没有数据,也会阻塞;通道可以被关闭,如果发送者尝试向一个已经关闭了的通道发送数据,那么将会触发panic,接受者如果尝试从已关闭的通道中获取数据时,那么会返回对应类型的零值和False;
channel的底层其实就是一个循环队列加一个锁,并且维护两个链表,一个是阻塞的接受者链表,一个阻塞的发送者链表;接受者和发送者就是对封装了g的结构;
当发送者在发送数据的时候,首先当然是要判断该通道是否为nil,是否被关闭;然后判断是否有阻塞的接受者,有的话就将队首摘出来,然后直接写入到接受者中;没有的话接着在判断缓存是否满,未满写入缓存中,以上不满足则阻塞当前g;
接受者接收数据的流程同发送者一样,都是先判断是否有发送者,有的话从链表中摘出来直接从发送者那么获取数据,没有发送者的话就判断是否有缓存,没有的话就阻塞;
channel有缓冲和无缓存的区别,正如上面介绍中说到的,如果为无缓冲,那么在发送或者接受数据的时候,就只会判断是否有接受者或者发送者,有的话直接取出并进行操作,没有的话就阻塞;而有缓冲的channel除了以上判断之外,如果没有对应的接受者和发送者,就会将数据写入缓冲中,若缓冲也满或为空才进入阻塞。
在关闭channel的时候会释放所有的接受者和发送者队列;所以如果有写者处于阻塞,那么在关闭之后写者会唤醒,对已关闭的chan进行写,引发panic
在使用Go channel的时候,一个适用的原则是不要从接收端关闭channel,也不要关闭有多个并发发送者的channel。换句话说,如果sender(发送者)只是唯一的sender或者是channel最后一个活跃的sender,那么你应该在sender的goroutine关闭channel,从而通知receiver(s)(接收者们)已经没有值可以读了。维持这条原则将保证永远不会发生向一个已经关闭的channel发送值或者关闭一个已经关闭的channel。
读写nil channel会永久阻塞当前goroutine