go channel底层实现
参考解析
题目来源:拼多多
答案:ORVR
在了解channel底层实现前,先得明白go的并发模型,在其他的语言中java、Python中的并发模型基本都是基于操作系统的线程,并发执行单元(线程)之间的通信一般就是用操作系统提供的线程或进程间的通信原语,如共享内存、信号、管道、消息队列、套接字等,最广泛的则是共享内存的并发模型
go语言从程序设计当初,就是解决上面传统并发模型作为模型,引入 CSP(Communicationing Sequential Processes-通信顺序进程)并发模型。
而在其中实现通信的就是channel
//下面就是channel的源码,由一个hchan实现类
type hchan struct {
// chan 里元素数量
qcount uint
// chan 底层循环数组的长度
dataqsiz uint
// 指向底层循环数组的指针
// 只针对有缓冲的 channel
buf unsafe.Pointer
// chan 中元素大小
elemsize uint16
// chan 是否被关闭的标志
closed uint32
// chan 中元素类型
elemtype *_type // element type
// 已发送元素在循环数组中的索引
sendx uint // send index
// 已接收元素在循环数组中的索引
recvx uint // receive index
// 等待接收的 goroutine 队列
recvq waitq // list of recv waiters
// 等待发送的 goroutine 队列
sendq waitq // list of send waiters
// 保护 hchan 中所有字段
lock mutex
}
channel底层结构图
buf指向一个底层的循环数组,只有设置为有缓存的channel才会有buf
sendx和recvx分别指向底层循环数组的发送和接收元素位置的索引
sendq和recvq分别表示发送数据的被阻塞的goroutine和读取数据的goroutine,这两个都是一个双向链表结构
sendq和recvq 的结构为 waitq 类型,sudog是对goroutine的一种封装
type waitq struct {
first *sudog
last *sudog
}
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满时会被阻塞 |