编辑
2023-12-04
后端
00
请注意,本文编写于 523 天前,最后修改于 523 天前,其中某些信息可能已经过时。

Go Channel底层原理及源码解析

Channel是Go语言中用于协程之间通信的重要机制。它的底层实现原理相对简单,通过分析源码可以更好地理解其工作原理。

go
type 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时不存在并发竞争问题 }

image.png

  1. Channel的基本概念

Channel可以看作是一个先进先出(FIFO)的队列,用于协程之间传输数据。它是协程安全的,可以在多个协程之间进行数据的发送和接收。Channel的设计理念是"不要通过共享内存来通信,而应通过通信来共享内存",体现了Go语言并发的思想

  1. Channel的底层结构

Channel的底层结构由三个FIFO队列组成:

  • buf循环队列:用于存放接收的数据,大小固定。
  • sendq待发送者队列:存放等待发送数据到Channel的协程的双向链表。
  • recvq待接收者队列:存放等待从Channel读取数据的协程的双向链表。
  1. Channel的操作

Channel的操作包括发送数据、接收数据和关闭Channel。

  • 发送数据:使用<-操作符将数据发送到Channel中,例如ch <- data
  • 接收数据:使用<-操作符从Channel中接收数据,例如data := <-ch
  • 关闭Channel:使用close(ch)函数关闭Channel,表示不再向Channel发送数据。
  1. Channel的数据传递

Channel传递数据的本质是值拷贝。当从缓冲区buf中读取数据时,会将数据从缓冲区的地址拷贝到接收者的栈内存地址;当从发送者的栈内存地址发送数据到缓冲区时,也会进行地址拷贝。对于引用类型的数据,传递的是地址拷贝

  1. Channel的并发安全性

对于Channel的参数修改是不是并发安全的,包括对三个队列及其他参数的访问,需要加锁来保证并发安全性。因此,可以将Channel看作是一个有锁队列

  1. Channel的性能

Channel的性能与sync.Mutex相当,没有明显的优势。Go语言推荐使用Channel进行并发协程的数据交互,是因为Channel的设计理念能够让程序变得简单,在大型程序和高并发复杂的运行状况中也能表现出良好的性能


Learn more:

  1. 深入分析Go1.18 Channel底层原理-腾讯云开发者社区-腾讯云
  2. Go channel底层结构实现原理分析及操作详解
  3. Go channel底层原理与使用实例 - 掘金

本文作者:yowayimono

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!