彻底搞懂channel原理(二)

上一篇文章主要介绍channel运行时是通过hchan表示的,也简单说明了hchan各个字段的含义。

我们提到,对channel的操作,本质上就是对hchan里字段的操作。因为在操作的过程中使用了互斥锁,所以保证了channel的并发安全。

这篇文章主要通过现实生活的一些例子来说明channel的一些原理,当然还是不会涉及过多源码。

无缓冲

我们都知道,channel分为无缓冲和缓冲。这两者最大的区别是什么?

我们用一个现实生活的快递例子来说明。

7faf64d59eb2ab30f12f86ea5c8b62e0.png

上面场景是快递员在等小库,当然反过来小库也可能在等快递员。

0b57cb4a120294d1986a0f453409e327.png

如果没有快递柜,快递员在送快递的过程中,如果家里没人,他就得在那等着,等着有人来签收快递,他才送货结束。

客户在快递员到来之前,他也不能离开家,不然快递来了没人收,所以他也得等到快递员上门,签字收了快递,他才算收货结束。

当然,客户不止有这家快递,如果快递员A在等的时候又来一个快递员B给他送货。这个快递员B不仅得等着,还得排队。等到客户到家后,肯定是先签收A的快递,然后再签收B的快递。

对应到无缓冲channel

发送数据的时候,如果没有对应的接收者ready,那么发送者就进入到等待发送队列中,等待有对应的接收者唤醒它。

接收数据的时候,如果没有对应的发送者ready,那么接收者就进入到等待接收队列中,等待有对应的发送者唤醒它。

还记得上一篇文章我们介绍过hchan的结构吗。

28f0ba579f3f928ed8404fe670f93852.png

其中recvq表示等待接收消息的队列,sendq表示等待发送消息的队列。

我们来看waitq

0073b4754c810a796d2a4fbe041907d0.png

ecd8b2261fc9fc86ac9c9b9d6975b9dd.png

本质上waitq就是一个链表,更确切的说是一个双向循环的链表。其中waitq记录了链表的头尾,sudog记录了当前等待者的上一个等待者(prev)和下一个等待者(next)。

这就好像小库在签收完A的快递后喊,下一个是谁啊?

A会说:我的下一个是B。

B会说:是我。我记得我上一个是A,目前我没有下一个,所以我是最后一个。

缓冲

看完了无缓冲队列,我们再来看缓冲队列。还是用上面的故事,

b43e2a3fbc86e84ebeaebde1779af39e.png

只要快递柜有空闲柜子,快递员就可以直接把快递放到柜子里,让客户自己去柜子拿。如果发送没有空闲的柜子,那就只能等,等到别人告诉我有空闲柜子,我再把快递放到空出来的柜子里。

对应到缓冲channel,上面的快递柜,就是缓冲channel中存储数据的buffer

对于发送者来说:只要缓冲区未满,发送者就可以继续发送数据存放在缓冲区。一旦缓冲区满了,发送者就只能进入到等待发送队列中,等待有对应的接收者唤醒它,然后它再把数据放入到刚刚被取走数据的位置。

对于接收者来说:只要缓冲区不为空,接收者就可以继续接收数据。一旦缓冲区空了,那么接收者就只能进入到等待接收队列中,等待有对应的发送者唤醒它。

上面还有什么问题吗?还真有。

我们取快递的时候,你一定会按照快递放入到快递柜的先后顺序取快递吗?咋么可能。

但是在channel中,是会保证消息的先进先出(FIFO)关系的。至于咋么保证的,我们终结篇解析代码细节的时候再说。

总结

这篇文章主要通过一个快递的例子来介绍channel操作的原理。下一篇我们介绍channel针对上述处理的细节逻辑。

推荐阅读:

Golang 入门笔记 - Channel

资料下载

点击下方卡片关注公众号,发送特定关键字获取对应精品资料!

  • 回复「电子书」,获取入门、进阶 Go 语言必看书籍。

  • 回复「视频」,获取价值 5000 大洋的视频资料,内含实战项目(不外传)!

  • 回复「路线」,获取最新版 Go 知识图谱及学习、成长路线图。

  • 回复「面试题」,获取四哥精编的 Go 语言面试题,含解析。

  • 回复「后台」,获取后台开发必看 10 本书籍。

对了,看完文章,记得点击下方的卡片。关注我哦~ 👇👇👇

如果您的朋友也在学习 Go 语言,相信这篇文章对 TA 有帮助,欢迎转发分享给 TA,非常感谢!7ac1e8f3e2e5da9f7fe4efdf420ea493.png

Go 中的 Channel 是并发编程中非常重要的一种数据结构,它提供了一种通信机制,使得不同的 Goroutine 可以在不使用显式锁的情况下进行数据交换。那么它的底层实现原理是什么呢? Go 中的 Channel 的底层实现是基于 Goroutine 和同步原语的。每个 Channel 都有一个相关联的等待队列,它用来存储等待在 Channel 上的 Goroutine。当一个 Goroutine 向 Channel 写入数据时,如果当前 Channel 中没有空闲的缓存区,那么这个 Goroutine 就会被阻塞,并被加入到等待队列中。如果有其他 Goroutine 从 Channel 中读取了数据,那么这个等待队列中的 Goroutine 就会被唤醒,继续执行。 类似地,当一个 Goroutine 从 Channel 中读取数据时,如果当前 Channel 中没有数据可读,那么这个 Goroutine 就会被阻塞,并被加入到等待队列中。如果有其他 Goroutine 向 Channel 中写入了数据,那么这个等待队列中的 Goroutine 就会被唤醒,继续执行。 需要注意的是,Go 中的 Channel 是类型安全的。也就是说,一个只能接收字符串的 Channel 是不能接收整数的。这是因为 Channel 内部维护了一个元素类型的信息,它会在运行时进行检查,确保只有符合类型要求的数据才能被写入或读取。 总之,Go 中的 Channel 的底层实现是基于 Goroutine 和同步原语的,并且是类型安全的。它提供了一种非常方便的并发编程机制,使得不同的 Goroutine 可以在不使用显式锁的情况下进行数据交换。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值