专业编程基础技术教程

网站首页 > 基础教程 正文

Golang-channel底层实现

ccvgpt 2024-08-06 12:51:47 基础教程 10 ℃

channel结构体的源码位于/runtime/chan.go中

channel是Go语言内置的核心类型,可以将其看做一个管道,channel和goroutine一起为go并发编程提供了最优雅和便利的方案

Golang-channel底层实现

在Go中有一句经典名言,永远不要通过共享内存来通信,而是要通过通信来共享内存,channel便是用于实现goroutine间通信的

channel提供了三种类型

1.单向只能发送:chan<- struct{} 只能发送

2.单向只能接收:<-chan struct{} 只能从chan里接收

3.双向即可发送也可接收:chan struct{} 既能接收也能发送

nil是channel的零值,对值是nil的channel发送和接收总是会阻塞


  • buf是带缓冲的channle所特有的结构,是个循环链表,用来存储缓存数据
  • sendxrecvx是用于记录buf中发送和接收的index
  • lock是个互斥锁,目的是为了保证goroutine以先进先出FIFO的方式进入结构体
  • recvqsendq分别是往channel接收或发送数据的goroutine所抽象出来的数据结构,是个双向链表


  • 创建channel实际上就是在内存中实例化了一个hchan结构体,并返回一个chan指针
  • channel在函数间传递都是使用的这个指针,这就是为什么函数传递中无需使用channel的指针,而是直接用channel就行了,因为channel本身就是一个指针

channel发送和接收数据:

1.有缓冲channel

发送数据前,会先锁住hchan这个结构体,然后逐步往buf中填充数据(从goroutine中copy数据到buf),然后解锁

接收数据前,同样会先锁住hchan这个结构体,然后逐步从buf中获取数据(buf中copy数据到goroutine),然后解锁

2.有缓冲channel数据满了怎么办

当有缓冲channel数据满了,当goroutine继续发送数据主动调用Go的调度器,让当前goroutine等待,并且让出内核线程M,交给其他goroutine使用,同时channel也会被抽象成含有指针和send元素的sudog结构体,保存到*sendq中等待被唤醒,那当前goroutine什么时候被唤醒呢?在有其他goroutine接收数据后被唤醒

3.有缓冲channel为空的情况

当channel中无数据时,goroutine接收数据操作会主动调用Go的调度器,让当前协程等待,并且让出内核线程M,交给其他goroutine使用,同时channel也会被抽象成含有指针和send元素的sudog结构体,保存到*recvq中等待有其他goroutine写入数据时被唤醒

此时,如果channel中有数据写入数据,会发现并没有锁住channel,然后将数据放入buf中,而是直接将数据从发送goroutine copy到了当前goroutine,这种方式非常好,在唤醒当前goroutine的过程中,无需再获得channel的锁,然后从buf中取数据,减少了内存cpoy,提高了效率

4.无缓冲channel

发送数据时,先查看到*recvq中是否有等待的goroutine,如果有直接拷贝数据,唤醒接收的goroutine,没有则保存到*sendq中等待被唤醒

接收数据时,同样先查看到*sendq中是否有等待的goroutine,如果有直接拷贝数据,唤醒发送的goroutine,没有则保存到*sendq中等待被唤醒

Tags:

最近发表
标签列表