详解Golang中channel的实现
作者:mjiarong
1、channel的基本概念
channel俗称管道,用于数据传递或数据共享,其本质是一个先进先出的队列,使用goroutine+channel进行数据通讯简单高效,同时也线程安全,多个goroutine可同时修改一个channel,不需要加锁。
channel可分为三种类型:
- 只读channel:只能读channel里面数据,不可写入
- 只写channel:只能写数据,不可读
- 一般channel:可读可写
2、channel的数据结构
type hchan struct { qcount uint // 队列中元素个数 dataqsiz uint // 循环队列的大小 buf unsafe.Pointer // 指向循环队列 elemsize uint16 // 通道里面的元素大小 closed uint32 // 通道关闭的标志 elemtype *_type // 通道元素的类型 sendx uint // 待发送的索引,即循环队列中的队尾指针front recvx uint // 待读取的索引,即循环队列中的队头指针rear recvq waitq // 接收等待队列 sendq waitq // 发送等待队列 lock mutex // 互斥锁 }
3、channel的hchan结构图
hchan结构体中的buf指向一个数组,用来实现循环队列,sendx是循环队列的队尾指针,recvx是循环队列的队头指针。dataqsize是缓存型通道的大小,qcount是记录通道内元素个数。
循环队列一般使用空余单元法来解决队空和队满时候都存在font=rear带来的二义性问题,但这样会浪费一个单元。golang的channel中是通过增加qcount字段记录队列长度来解决二义性,一方面不会浪费一个存储单元,另一方面当使用len函数查看队列长度时候,可以直接返回qcount字段,一举两得。
hchan结构体中另一重要部分是recvq,sendq,分别存储了等待从通道中接收数据的goroutine,和等待发送数据到通道的goroutine。两者都是waitq类型。sudog是对goroutine的一种封装
type waitq struct { first *sudog last *sudog }
waitq是一个结构体类型,waitq和sudog构成双向链表,其中sudog是链表元素的类型,waitq中first和last字段分别指向链表头部的sudog,链表尾部的sudog。
channel 的发送和接收操作本质上都是 “值的拷贝”,无论是从 sender goroutine 的栈到 chan buf,还是从 chan buf 到 receiver goroutine,或者是直接从 sender goroutine 到 receiver goroutine。
channel读取写入流程
操作 | nil channel | closed channel | not nil,not closed |
---|---|---|---|
close | panic | panic | 正常关闭 |
读<-ch | 阻塞 | 如果channel关闭前有数据,则会正常读取到数据.如果没有数据则会读取到对应元素类型的空值 | 阻塞或正常读取数据,缓冲型channel为空或非缓冲型channel等待发送者时会阻塞 |
写ch<- | 阻塞 | panic | 阻塞或正常写入数据,非缓冲型channel等待接收者或缓冲型channel buf满时会被阻塞 |
到此这篇关于详解Golang中channel的实现的文章就介绍到这了,更多相关Golang channel实现内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!