0
点赞
收藏
分享

微信扫一扫

golang中channel的实现原理

奔跑的酆 2022-02-16 阅读 89

上一次,研究了一下map的实现原理,原文链接:golang中map的实现原理_初级程序猿一步步进阶高级的博客-CSDN博客

这次我们来研究一下,channel的实现原理。

1、初始化一个channel变量

ch := make(chan int, 10)

2、make后,也会调用对应的方法

        当make初始化chan类型的数据时,会调用makechan64()方法,在这个方法中会再调用makechan()方法,返回了一个hchan这个数据类型的地址。*注意,重点来了,chan类型的核心就是hchan这个结构体,后面我们会看hchan里有什么,以及它的核心逻辑是什么。

func makechan64(t *chantype, size int64) *hchan {
	if int64(int(size)) != size {
		panic(plainError("makechan: size out of range"))
	}

	return makechan(t, int(size))
}


func makechan(t *chantype, size int) *hchan {
	// 为了方便理解省略部分代码,感兴趣可以查看这部分源码,在这里主要想展现的是makechan方法返回的其实是hchan这个结构体的地址

	var c *hchan // 关键代码
	switch {
	default:
		// Elements contain pointers.
		c = new(hchan) //关键代码
		c.buf = mallocgc(mem, elem, true)
	}

	c.elemsize = uint16(elem.size)
	c.elemtype = elem
	c.dataqsiz = uint(size)

	return c
}

3、hchan结构体的介绍

        截图来自源码文件:runtime/chan.go文件,感兴趣的可以亲自去看读一下。

        qcount:已经存储了多少元素

         buf:缓冲区的地址,比如 ch <- 5,会把5存在这个缓冲区

        dataqsiz:最多存储多少元素

         elemsize:每个元素占多少空间

        closed: 表示chan是否关闭

        sendx:表示当前写入的位置

        recvq:表示读的位置

        sendq:写满了以后会阻塞,这时会存放在队列,以待被唤醒执行

        recvq:读队列与上相同逻辑,没有数据时也会阻塞等待被唤醒执行

        lock:这个是非常重要的,channel具有锁机制,所以channel是安全的。

4、hchan是如何工作的?

        当我们使用make初始化后,就指定了缓冲区可以存储多少个数据。比如3个

ch := make(chan int, 3)

        这时候sendx和recvq都指在这个缓冲区 的0的位置,像这样

        现在给ch增加两个数据,

ch <- 1
ch <- 2

        就会变成这样,sendx指向位置往后移动,sendx指向的位置就是下次写入数据的位置。

         如果我再写入两个数据3和4,这时候sendx走到头之后又会再从0开始,但是很不巧0的位置已经有数据了,所以只有3被写入了,4就会阻塞,阻塞的情况就会被记录到写的队列-sendq。

ch <- 3
ch <- 4

 

再来看看读,如果我们读了2个数据,recvq就会往后移动两个位置,同时因为读把0的位置空出来了,写的队列会被激活,把4放入0的位置上。

<- ch
<- ch

 

读到最后,也会重新从0开始读,它们是在这个缓冲区循环往复的读和写,再配合着两个队列和锁保证它的可靠性。

以上就是chan类型的基本运行原理了。

举报

相关推荐

0 条评论