Channel是Go语言中用于协程之间通信的重要机制。它的底层实现原理相对简单,通过分析源码可以更好地理解其工作原理。
gotype hchan struct {
//channel分为无缓冲和有缓冲两种。
//对于有缓冲的channel存储数据,借助的是如下循环数组的结构
qcount uint // 循环数组中的元素数量
dataqsiz uint // 循环数组的长度
buf unsafe.Pointer // 指向底层循环数组的指针
elemsize uint16 //能够收发元素的大小
closed uint32 //channel是否关闭的标志
elemtype *_type //channel中的元素类型
//有缓冲channel内的缓冲数组会被作为一个“环型”来使用。
//当下标超过数组容量后会回到第一个位置,所以需要有两个字段记录当前读和写的下标位置
sendx uint // 下一次发送数据的下标位置
recvx uint // 下一次读取数据的下标位置
//当循环数组中没有数据时,收到了接收请求,那么接收数据的变量地址将会写入读等待队列
//当循环数组中数据已满时,收到了发送请求,那么发送数据的变量地址将写入写等待队列
recvq waitq // 读等待队列
sendq waitq // 写等待队列
lock mutex //互斥锁,保证读写channel时不存在并发竞争问题
}
Channel可以看作是一个先进先出(FIFO)的队列,用于协程之间传输数据。它是协程安全的,可以在多个协程之间进行数据的发送和接收。Channel的设计理念是"不要通过共享内存来通信,而应通过通信来共享内存",体现了Go语言并发的思想
Channel的底层结构由三个FIFO队列组成:
Channel的操作包括发送数据、接收数据和关闭Channel。
<-
操作符将数据发送到Channel中,例如ch <- data
。<-
操作符从Channel中接收数据,例如data := <-ch
。close(ch)
函数关闭Channel,表示不再向Channel发送数据。Channel传递数据的本质是值拷贝。当从缓冲区buf中读取数据时,会将数据从缓冲区的地址拷贝到接收者的栈内存地址;当从发送者的栈内存地址发送数据到缓冲区时,也会进行地址拷贝。对于引用类型的数据,传递的是地址拷贝
对于Channel的参数修改是不是并发安全的,包括对三个队列及其他参数的访问,需要加锁来保证并发安全性。因此,可以将Channel看作是一个有锁队列
Channel的性能与sync.Mutex
相当,没有明显的优势。Go语言推荐使用Channel进行并发协程的数据交互,是因为Channel的设计理念能够让程序变得简单,在大型程序和高并发复杂的运行状况中也能表现出良好的性能
Learn more:
本文作者:yowayimono
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!