Go Channel (底层实现逻辑)

Go Channel (底层实现逻辑)

​ 通道(channel)是Go语言中提供协程间通信的独特方式,传统的多线程编程比较困难,常常需要开发者了解一些底层的细节(例如互斥锁、条件变量及内存屏障等)。而通过通道交流的方式,Go语言屏蔽了底层实现的诸多细节,使得并发编程更加简单快捷。将通道作为Go语言中的一等公民,是Go语言遵循CSP并发编程模式的结果,这种模型最重要的思想是通过通道来传递消息。同时,通道借助Go语言调度器的设计,可以高效实现通道的堵塞/唤醒,进一步实现通道多路复用的机制。

不要通过共享内存来通信,通过通信来共享内存。

​ ——罗勃·派克

Channel读取、接收数据流程

在这里插入图片描述

Channel源代码分析

channel运行时数据结构

其中sendx指向了当前buf(通道的缓冲区)中的发送序号,若sendx等于dataqsiz(通道循环队列)的大小,则表示当前buf中已经被置满,此时sendx置0,开启下一轮向buf的循环写入。其中recvx指向了当前buf中的接收序号,若recvx等于dataqsiz(通道循环队列)的大小,则表示当前buf中最后一个数据已经被取出,此时recvx置0,开启下一轮从buf的循环读取。

// 通道在运行时是一个特殊的hchan结构体
type hchan struct {
   
   
   // 当前通道队列中的数据个数
   qcount uint // total data in the queue
   // 循环队列的大小
   dataqsiz uint // size of the circular queue
   // 数据缓冲区
   // 写入时:如果读取等待队列中没有正在等待的协程,但是该通道是带缓冲区的,并且当前缓冲区没有满,则向当前缓冲区中写入当前元素。
   // 读取时:如果队列中没有正在等待写入的协程,但是该通道是带缓冲区的,并且当前缓冲区中有数据,则读取该缓冲区中的数据,并将数据写入当前的读取协程中。
   buf unsafe.Pointer // points to an array of dataqsiz elements
   // 通道中元素的大小
   elemsize uint16
   // 通道是否关闭
   closed uint32
   // 通道元素的类型
   elemtype *_type // element type
   // 记录buf中的发送序号
   sendx uint 
   // 记录buf中的接收序号
   recvx uint 
   // 读取的堵塞协程队列,每个协程对应一个sudog结构,它是对协程的封装,包含了准备获取的写成中的元素指针等
   // 当通道无缓冲区或者当前缓冲区没有数据则代表当前协程的sudog结构需要放入recvq链表末尾,并且当前协程陷入休眠状态,等待被唤醒重新执行
   // 写入时:当有读取的协程正在等待时,直接从该协程链表中,获取第一个协程,并将元素直接复制到对应的协程中,同时唤醒被堵塞的协程
   recvq waitq 
   // 写入的阻塞协程队列,
   // 若当前通道无缓冲区或者当前缓冲区已满,则代表当前协程的sudog结构需要放入sendq链表末尾中,并且当前协程陷入休眠状态,等待被唤醒重新执行
   // 读取时:当有等待写入的协程时,直接从等待的写入的协程链表中获取第一个协程,并将写入的元素直接复制到当前协程中,同时唤醒被堵塞的写入协程
   sendq waitq 
   // 锁,并发保护
   lock mutex
}

channel运行时数据结构hchan的创建

// chan 初始化函数  t 表示通道类型,size 代表通道中元素的大小。
// 当分配的大小为0时,只用在内存中分配hchan结构体大小即可
// 当通道的元素中不包含指针时,连续分配hchan结构体大小+size元素大小,当通道的元素中包含指针时,需要单独分配内存空间,
// 因为当元素中包含指针时,需要单独分配空间才能正常进行垃圾回收
func makechan(t *chantype, size int) *hchan {
   
   
	elem := t.elem

	// compiler checks this but be safe.
	if elem.size >= 1<<16 {
   
   
		throw("makechan: invalid channel element type")
	}
	if hchanSize%maxAlign != 0 || elem.align > maxAlign {
   
   
		throw("makechan: bad alignment")
	}
	// 计算需要为通道中元素分配的大小
	mem, overflow := math.MulUintptr(elem.size, uintptr(size))
	if overflow || mem > maxAlloc-hchanSize || size < 0 {
   
   
		panic(plainError("makechan: size out of range"))
	}
    
	var c *hchan
	switch {
   
   
	case mem == 0:// 无缓冲通道
		// Queue or element size is zero.
		c = (*hchan)(mallocgc(hchanSize, nil, true))
		// Race detector uses this location for synchronization.
		c.buf = c.raceaddr()
	case elem.ptrdata == 0:// 有缓冲,元素为非指针类型
		// Elements do not contain pointers.
		// Allocate hchan and buf in one call.
		c = (*hchan)(mallocgc(hchanSize+mem, nil, true))
		c.buf = add(unsafe.Pointer
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值