go Channel原理 (三)

Channel

设计原理

不要通过共享内存的方式进行通信,而是应该通过通信的方式共享内存。

在主流编程语言中,多个线程传递数据的方式一般都是共享内存。
在这里插入图片描述
Go 可以使用共享内存加互斥锁进行通信,同时也提供了一种不同的并发模型,即通信顺序进程(Communicating sequential processes,CSP)。Goroutine 和 Channel 分别对应 CSP 中的实体和传递信息的媒介,Goroutine 之间会通过 Channel 传递数据。在这里插入图片描述
上图中的两个 Goroutine,一个会向 Channel 中发送数据,另一个会从 Channel 中接收数据,它们两者能够独立运行并不存在直接关联,但是能通过 Channel 间接完成通信。

接收数据

两个 Goroutine,一个会向 Channel 中发送数据,另一个会从 Channel 中接收数据,它们两者能够独立运行并不存在直接关联,但是能通过 Channel 间接完成通信。这是一个 生产者 - 消费者 模型,负责接收数据的 goroutine 从 channel 读取一个消息进行消费,channel 起到一个临界区/缓冲区的作用。

// chanrecv 函数接收 channel c 的元素并将其写入 ep 所指向的内存地址。
// 如果 ep 是 nil,说明忽略了接收值。
// 如果 block == false,即非阻塞型接收,在没有数据可接收的情况下,返回 (false, false)
// 否则,如果 c 处于关闭状态,将 ep 指向的地址清零,返回 (true, false)
// 否则,用返回值填充 ep 指向的内存地址。返回 (true, true)
// 如果 ep 非空,则应该指向堆或者函数调用者的栈func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) {// 省略 debug 内容 …………// 如果是一个 nil 的 channelif c == nil {// 如果不阻塞,直接返回 (false, false)if !block {return}// 否则,接收一个 nil 的 channel,goroutine 挂起gopark(nil, nil, "chan receive (nil chan)", traceEvGoStop, 2)// 不会执行到这里throw("unreachable")}// 在非阻塞模式下,快速检测到失败,不用获取锁,快速返回// 当我们观察到 channel 没准备好接收:// 1. 非缓冲型,等待发送列队 sendq 里没有 goroutine 在等待// 2. 缓冲型,但 buf 里没有元素// 之后,又观察到 closed == 0,即 channel 未关闭。// 因为 channel 不可能被重复打开,所以前一个观测的时候 channel 也是未关闭的,// 因此在这种情况下可以直接宣布接收失败,返回 (false, false)if !block && (c.dataqsiz == 0 && c.sendq.first == nil ||c.dataqsiz > 0 && atomic.Loaduint(&c.qcount) == 0) &&atomic.Load(&c.closed) == 0 {return}var t0 int64if blockprofilerate > 0 {t0 = cputicks()}// 加锁lock(&c.lock)// channel 已关闭,并且循环数组 buf 里没有元素// 这里可以处理非缓冲型关闭 和 缓冲型关闭但 buf 无元素的情况// 也就是说即使是关闭状态,但在缓冲型的 channel,// buf 里有元素的情况下还能接收到元素if c.closed != 0 && c.qcount == 0 {if raceenabled {raceacquire(unsafe.Pointer(c))}// 解锁unlock(&c.lock)if ep != nil {// 从一个已关闭的 channel 执行接收操作,且未忽略返回值// 那么接收的值将是一个该类型的零值// typedmemclr 根据类型清理相应地址的内存typedmemclr(c.elemtype, ep)}// 从一个已关闭的 channel 接收,selected 会返回truereturn true, false}// 等待发送队列里有 goroutine 存在,说明 buf 是满的// 1. 非缓冲型的 channel。直接进行内存拷贝(从 sender goroutine -> receiver goroutine)// 2. 缓冲型的 channel,但 buf 满了。接收到循环数组头部的元素,并将发送者的元素放到循环数组尾部if sg := c.sendq.dequeue(); sg != nil {// Found a waiting sender. If buffer is size 0, receive value// directly from sender. Otherwise, receive from head of queue// and add sender's value to the tail of the queue (both map to// the same buffer slot because the queue is full).recv(c, sg, ep, func() { unlock(&c.lock) }, 3)return true, true}// 缓冲型,buf 里有元素,可以正常接收if c.qcount > 0 {// 直接从循环数组里找到要接收的元素qp := chanbuf(c, c.recvx)// …………// 没有忽略要接收的值,不是 "<- ch",而是 "val <- ch",ep 指向 valif ep != nil {typedmemmove(c.elemtype, ep, qp)}// 清理掉循环数组里相应位置的值typedmemclr(c.elemtype, qp)// 接收游标向前移动c.recvx++// 接收游标归零if c.recvx == c.dataqsiz {c.recvx = 0}// buf 数组里的元素个数减 1c.qcount--// 解锁unlock(&c.lock)return true, true}if !block {// 非阻塞接收,解锁。selected 返回 false,因为没有接收到值unlock(&c.lock)return false, false}// 构造一个 sudog 并设置相应参数gp := getg()mysg := acquireSudog()mysg.releasetime = 0if t0 != 0 {mysg.releasetime = -1}mysg.elem = epmysg.waitlink = nilgp.waiting = mysgmysg.g = gpmysg.selectdone = nilmysg.c = cgp.param = nil// 进入 channel 的等待接收队列c.recvq.enqueue(mysg)// 将当前 goroutine 挂起goparkunlock(&c.lock, "chan receive", traceEvGoBlockRecv, 3)// 被唤醒了,接着从这里继续执行一些扫尾工作if mysg != gp.waiting {throw("G waiting list is corrupted")}gp.waiting = nilif mysg.releasetime > 0 {blockevent(mysg.releasetime-t0, 2)}closed := gp.param == nilgp.param = nilmysg.c = nil// 释放当前 gorountine 的 sudogreleaseSudog(mysg)return true, !closed
}func recv(c *hchan, sg *sudog, ep unsafe.Pointer, unlockf func(), skip int) {// 如果是非缓冲型的 channelif c.dataqsiz == 0 {if raceenabled {racesync(c, sg)}// 未忽略接收的数据 不是 "<- ch",而是 "val <- ch",ep 指向 valif ep != nil {// 直接拷贝数据,从 sender goroutine -> receiver goroutinerecvDirect(c.elemtype, sg, ep)}} else {// 缓冲型的 channel,但 buf 已满。// 1. 循环数组 buf 队首的元素拷贝到接收数据的地址// 2. 将 sender 的数据入队。qp := chanbuf(c, c.recvx)// …………// 将 recvx 处的数据拷贝给接收者if ep != nil {typedmemmove(c.elemtype, ep, qp)}// sender data -> buftypedmemmove(c.elemtype, qp, sg.elem)// 更新索引c.recvx++if c.recvx == c.dataqsiz {c.recvx = 0}c.sendx = c.recvx}sg.elem = nilgp := sg.g// 解锁unlockf()gp.param = unsafe.Pointer(sg)if sg.releasetime != 0 {sg.releasetime = cputicks()}// 将当前处理器的 runnext 设置成发送数据的 Goroutine,在调度器下一次调度时将阻塞的发送方唤醒。goready(gp, skip+1)
}func recvDirect(t *_type, sg *sudog, dst unsafe.Pointer) {// dst is on our stack or the heap, src is on another stack.src := sg.elemtypeBitsBulkBarrier(t, uintptr(dst), uintptr(src), t.size)memmove(dst, src, t.size)
}

从 channel 接收消息 的 核心函数是 chanrecv

跟 send 流程差不多:
特殊情况:

  1. 如果 channel 为空,那么会直接调用 runtime.gopark 挂起当前 goroutine。
  2. 如果 channel 已经关闭并且缓冲区没有任何数据,runtime.chanrecv 会直接返回零值。
    正常情况:
  3. 如果 channel 的 sendq 队列中存在挂起的 goroutine,会将 recvx 索引所在的数据拷贝到接收变量所在的内存空间上并将 sendq 队列中 goroutine 的数据拷贝到缓冲区。
  4. 如果 channel 的缓冲区中包含数据,那么直接读取 recvx 索引对应的数据。
  5. 在默认情况下会挂起当前的 goroutine,将 runtime.sudog 结构加入 recvq 队列并陷入休眠等待调度器的唤醒。
    从 channel 接收数据时,会触发 goroutine 调度的两个时机:
  6. 当 channel 为空时。
  7. 当缓冲区中不存在数据并且也不存在数据的发送者时。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/38731.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql8.0.19安装zip版本

下载地址https://downloads.mysql.com/archives/community/ 下载版本 下载后解压&#xff0c;不包括data 和my.ini文件。其中data 文件是自动生成的【mysqld --initialize --console】&#xff0c;my.ini需要自己编写设置。 新建my.ini文件 需要自己设置 basedirG:\soft\mysql…

内网服务器时间校正

新购买的云服务器发现内网机器和可以访问外网的机器时间慢了三分钟&#xff0c;导致有些访问会报错&#xff0c;那么我们配置一下ntp校正一下时间。外网配置起来比较简单&#xff0c;直接下载ntp执行校正命令即可。 比当前时间慢了三分钟 注意当前服务器是可以访问外网的机器这…

【gitee使用教程】(创建项目仓库并上传代码简易版)

gitee使用教程&#xff0c;创建项目仓库并上传代码简易版 1.在码云上创建一个仓库2.将代码克隆到本地1.复制仓库地址2.找到你想要放置的文件位置&#xff0c;右键点击更多选项&#xff0c;选择Git Clone3.将复制的仓库地址填入URL 3. IDEA结合GIT和Gitee的简单使用idea需要识别…

【python】最新版抖音s逆向拿到数据,非常详细教程(附完整代码)

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全…

Excel 宏录制与VBA编程 ——VBA编程技巧篇一 (Union方法、Resize方法、Cells方法、UseSelect方法、With用法)

Uniom方法 使用Union方法可以将多个非连续区域连接起来成为一个区域&#xff0c;从而可以实现对多个非连续区域一起进行操作。 Resize方法 使用Range对象的Resize属性调整指定区域的大小&#xff0c;并返回调整大小后的单元格区域。 Cells方法 Cells属性返回一个Range对象。 Us…

Domino应用中的HTML5

大家好&#xff0c;才是真的好。 在xpages多年不见有效更新&#xff0c;前景不明的时候&#xff0c;Domino传统Web应用开发方式还是受到了应有的青睐。毕竟&#xff0c;在Nomad Web时代&#xff0c;连最传统的Notes CS原生应用也突然焕发了勃勃生机一样。 但&#xff0c;对有…

ARP 原理详解 一

ARP 原理 ARP&#xff08;Address Resolution Protocol&#xff09;地址解析协议&#xff0c;是根据 IP 地址获取物理地址的一个 TCP/IP 协议。 OSI 网络七层模型中&#xff0c;IP 地址在 OSI 模型第三层&#xff0c;MAC 地址在第二层&#xff0c;彼此不直接通信。 在通过以…

性能测试中的场景设计和测试执行

假设一个内部系统要求响应时间在 3s 以内&#xff0c;支持最大用户数为4万。根据二八原则&#xff0c;80%用户在20%时间使用系统(4w80%)/(24h20%)≈1.9点击/秒。并发数TPS&#xff08;运行时间思考时间&#xff09;1.9&#xff08;30.50.330.50.30.53&#xff09;21。 注意&am…

Flutter循序渐进==>数据结构(列表、映射和集合)和错误处理

导言 填鸭似的教育确实不行&#xff0c;我高中时学过集合&#xff0c;不知道有什么用&#xff0c;毫无兴趣&#xff0c;等到我学了一门编程语言后&#xff0c;才发现集合真的很有用&#xff1b;可以去重&#xff0c;可以看你有我没有的&#xff0c;可以看我有你没有的&#xf…

毫米波雷达深度学习技术-1.7训练一个神经网络

1.7 训练一个神经网络 对于训练神经网络&#xff0c;有两个步骤&#xff0c;即前向传递和误差反向传播。 1.7.1 前向传播和反向传播 在前向传递中&#xff0c;输入被馈送到模型并与权重向量相乘&#xff0c;并为每一层添加偏差以计算模型的输出。密集层或全连接层第l层的输入、…

网络基础-RIP协议

RIP&#xff08;Routing Information Protocol&#xff09;是一个基于距离矢量的动态路由协议&#xff0c;常用于小型到中型网络。RIP是较早的路由协议之一&#xff0c;具有简单易用的特点。以下是关于RIP协议的详细介绍&#xff1a; RIP的主要特点 ①使用跳数&#xff08;ho…

非标设备行业的数智化项目管理

近年来&#xff0c;中国制造快速发展&#xff0c;企业迫切需要加快转型升级。与传统制造业相比&#xff0c;高端制造业具有明显的优势&#xff1a;高技术、高附加值、低污染、低排放、竞争优势强。一方面&#xff0c;企业对于生产效率和自动化水平的要求不断提高&#xff0c;期…

武汉星起航:成功挂牌上股交,领航亚马逊跨境电商,共创未来辉煌

在全球电商的竞争格局中&#xff0c;亚马逊凭借其卓越的服务、丰富的商品种类和高效的物流体系&#xff0c;始终稳坐全球电商市场的头把交椅。而在这股不可阻挡的电商浪潮中&#xff0c;武汉星起航电子商务有限公司凭借其前瞻性的战略布局和强大的运营能力&#xff0c;成功在20…

名企面试必问30题(十二)——简单介绍一下你的家庭情况

1.思路 对于面试官来说&#xff0c;他提出这个问题&#xff0c;只是为了深挖您的性格、稳定性、行事风格&#xff0c;包括未来定居规划、生育规划等基础信息&#xff0c;这是正常情况。您不要过多围绕其他家庭成员来讲&#xff0c;否则面试官无法获取他想要的&#xff0c;您也难…

【单片机毕业设计选题24040】-基于STM32的蓝牙防丢器设计

系统功能: 系统上电后显示“欢迎使用蓝牙防丢系统请稍后”两秒钟显示正常界面&#xff0c;如果蓝牙正常连接OLED显示Connected, 蓝牙未连接则显示DisConnected同时蜂鸣器报警 蓝牙正常连接后在APP上每隔三秒显示一个Connected 系统功能框图: 主要功能模块原理图: 电源时钟…

大数据之Zookeeper部署

文章目录 集群规划环境准备集群部署参考资料 集群规划 确定使用Hadoop101、hadoop102和hadoop103三台服务器来构建Zookeeper集群。 hadoop101hadoop102hadoop103zookeeperzookeeperzookeeper 环境准备 安装zookeeper前需要确保下面的环境配置成功&#xff0c;具体可以参考大…

AI智能剪辑发展到哪种地步?来看看云微客就知道了!

不是短视频团队招不起&#xff0c;而是矩阵账号更有性价比。企业做短视频有反思过为什么干不过同行吗&#xff1f;我们来看看大佬是怎么做的。云微客AI智能剪辑系统用几百个账号做矩阵布局&#xff0c;系统每天自动进行批量剪视频、写文案、一键自动化发布视频&#xff0c;一个…

[漏洞复现] MetInfo5.0.4文件包含漏洞

[漏洞复现] MetInfo5.0.4文件包含漏洞 MetInfo5.0.4 漏洞代码审计 漏洞出现在about/index.php中&#xff0c;因为利用了动态地址&#xff0c;所以存在漏洞。 漏洞检查语句&#xff08;&#xff01;192.168.109.100是我的服务器ip&#xff0c;需要换成自己的&#xff09;&…

华为仓颉语言体验:一个简单的socket服务端实现

前言 由于仓颉目前是内测状态&#xff0c; 不能展示仓颉的详细信息&#xff0c;但是华为仓颉官网的公共文档的内容是可以公开的。 我相信有不少喜欢编程的朋友都申请了内测&#xff0c;但是一些编程初学者应该和我一样&#xff0c;处于摸索阶段。所以&#xff0c;我这里把我测…

中电金信:金Gien乐道 | 6月热门新闻盘点 回顾这一月的焦点事件

“以检之力 e企守护”——上海市检一分院与中电金信开展联学联建 6月24日&#xff0c;上海市人民检察院第一分院与中电金信数字科技集团股份有限公司联合开展“以检之力 e企守护”联学联建活动。双方共同参观了全国检察机关证券期货犯罪办案基地和重大职务犯罪案件办理&#xf…