golang中channel的实现原理

本文深入探讨了Go语言中channel的实现原理,从makechan方法的调用来解析hchan结构体,详细介绍了hchan的各个字段如qcount、buf、dataqsiz等,并通过例子解释了channel如何进行读写操作以及其内部的队列和锁机制,展示了channel在并发中的安全性和工作流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

这次我们来研究一下,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类型的基本运行原理了。

在Go语言中,channel是一种用于goroutine之间通信的重要机制。它可以在goroutine之间进行同步和数据传递。Channel可以被看作是一种先进先出的队列,每个元素都是一个数据类型。 Channel实现原理基于一种叫做CSP(Communicating Sequential Processes)的并发模型,该模型是由Tony Hoare提出的。CSP模型中,进程通过发送和接收消息来进行通信和同步。 Go语言中的channel也是基于CSP模型实现的,其底层是通过一个管道(pipe)来实现的。管道是一种特殊的文件,可以通过文件操作来进行读写,但是它不同于普通的文件,它是一种特殊的文件,只能用于进程间通信,而不是用于存储数据。 当我们创建一个channel时,实际上是创建了一个管道,该管道有两端,一个用于发送数据,一个用于接收数据。goroutine可以通过channel向管道中写入数据或从管道中读取数据,当管道中没有数据时,读取操作会被阻塞,直到有数据写入管道。同样地,当管道已满时,写入操作也会被阻塞,直到有数据从管道中被读取。 在Go语言中,channel有两种类型:有缓冲和无缓冲。无缓冲的channel可以保证数据的即时传输,当数据被写入channel时,写入操作会被阻塞,直到有一个goroutine从channel中读取了数据。有缓冲的channel可以缓存一定量的数据,当缓存满时,写入操作会被阻塞,直到有一个goroutine从channel中读取了数据,同样地,在缓存未满时,读取操作会被阻塞,直到有数据被写入到channel中。 总之,channel是Go语言中非常重要的并发机制,它可以保证多个goroutine之间的同步和通信。它的实现原理基于CSP模型,底层是通过管道来实现的。了解channel原理对于理解Go语言的并发编程非常重要。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是从宝畅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值