go channel底层实现

参考解析

题目来源:拼多多

答案:ORVR

在了解channel底层实现前,先得明白go的并发模型,在其他的语言中java、Python中的并发模型基本都是基于操作系统的线程,并发执行单元(线程)之间的通信一般就是用操作系统提供的线程或进程间的通信原语,如共享内存、信号、管道、消息队列、套接字等,最广泛的则是共享内存的并发模型

go语言从程序设计当初,就是解决上面传统并发模型作为模型,引入 CSP(Communicationing Sequential Processes-通信顺序进程)并发模型。

而在其中实现通信的就是channel

  1. //下面就是channel的源码,由一个hchan实现类
  2. type hchan struct {
  3. // chan 里元素数量
  4. qcount uint
  5. // chan 底层循环数组的长度
  6. dataqsiz uint
  7. // 指向底层循环数组的指针
  8. // 只针对有缓冲的 channel
  9. buf unsafe.Pointer
  10. // chan 中元素大小
  11. elemsize uint16
  12. // chan 是否被关闭的标志
  13. closed uint32
  14. // chan 中元素类型
  15. elemtype *_type // element type
  16. // 已发送元素在循环数组中的索引
  17. sendx uint // send index
  18. // 已接收元素在循环数组中的索引
  19. recvx uint // receive index
  20. // 等待接收的 goroutine 队列
  21. recvq waitq // list of recv waiters
  22. // 等待发送的 goroutine 队列
  23. sendq waitq // list of send waiters
  24. // 保护 hchan 中所有字段
  25. lock mutex
  26. }

channel底层结构图

buf指向一个底层的循环数组,只有设置为有缓存的channel才会有buf

sendx和recvx分别指向底层循环数组的发送和接收元素位置的索引

sendq和recvq分别表示发送数据的被阻塞的goroutine和读取数据的goroutine,这两个都是一个双向链表结构

sendq和recvq 的结构为 waitq 类型,sudog是对goroutine的一种封装

  1. type waitq struct {
  2. first *sudog
  3. last *sudog
  4. }

292.go channel底层实现 - 图1

channel 的发送和接收操作本质上都是 “值的拷贝”,无论是从 sender goroutine 的栈到 chan buf,还是从 chan buf 到 receiver goroutine,或者是直接从 sender goroutine 到 receiver goroutine。

channel读取写入流程

操作 nil channel closed channel not nil,not channel
close panic panic 正常关闭
读<-ch 阻塞 读到对应类型的零值 阻塞或正常读取数据,缓冲型channel为空或非缓冲型channel等待发送者时会阻塞
写ch<- 阻塞 panic 阻塞或正常写入数据,非缓冲型channel没有等待接收者或缓冲型channel buf满时会被阻塞