Go:关于 Channel

文章目录

  • 写在前面
  • 内容
    • 模型图与代码
    • 发送流程
    • 接收流程

写在前面

本篇主要是通过 Channel 的模型图,对 Channel 的原理做一个基本的概述

内容

模型图与代码

我们先来看下 Channel 的模型图:
在这里插入图片描述
以上的图是一个简要的模型图,意味着丢失一些细节,我们再结合源码来看下:

type hchan struct {// 以下是环形缓冲区qcount   uint           // total data in the queuedataqsiz uint           // size of the circular queuebuf      unsafe.Pointer // points to an array of dataqsiz elementselemsize uint16elemtype *_type // element type// Channel 的关闭状态closed   uint32// 以下是等待队列和接收队列sendx    uint   // send indexrecvx    uint   // receive indexrecvq    waitq  // list of recv waiterssendq    waitq  // list of send waiters// 以下是 Channel 的锁lock mutex
}

可以看到,模型图相比于源码,主要是少了 closed 和 lock 这两个属性。
从模型图里我们可以看到,Channel 的构造主要有三部分:

  • 发送队列
  • 接收队列
  • 缓冲区

因此我们经常遇到的问题就出现在这三部分是否有无的排列组合,例如发送队列里有等待协程,接收队列里有等待协程,无缓冲区这样。

接下来我们结合模型图,并分别从发送流程和接收流程来看 Channel 的一个基本运作流程。至于模型图里没有包括的部分(锁和关闭状态)就作为补充处理。
(注:Channel 的源码位于 runtime 包下的 chan.go 文件)

发送流程

发送流程里,主要涉及到的方法是

func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool

假设我们现在要发送一个数据,那么这个数据就可能有这么几种状态

  • 在发送等待队列里
  • 在缓冲区里
  • 被接收队列给拿走了

在源码里,发送数据的时候,总体流程上是

Created with Raphaël 2.3.0 检查发送等待队列 检查缓冲区 进入发送等待队列

我们可以看下:

func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool {...lock(&c.lock)// 向一个已经关闭的通道发送数据,会 panicif c.closed != 0 {unlock(&c.lock)panic(plainError("send on closed channel"))}// 会先检查接收队列里是否有等待接收的协程,如果有等待协程,我们就可以忽略缓冲区的两种情况:// - 没有缓冲区// - 缓冲区满// 那么意味着这里我们的数据是直接跟接收队列对接,既然接收队列里有等待协程,那就把数据给他就行了if sg := c.recvq.dequeue(); sg != nil {send(c, sg, ep, func() { unlock(&c.lock) }, 3)return true}// 到了这一步,就意味着不考虑接收队列了,因为没有等待协程可以用// 于是就看下是否有缓冲区了,有缓冲区的话,就把数据放到缓冲区即可if c.qcount < c.dataqsiz {// Space is available in the channel buffer. Enqueue the element to send.qp := chanbuf(c, c.sendx)if raceenabled {racenotify(c, c.sendx, nil)}typedmemmove(c.elemtype, qp, ep)c.sendx++if c.sendx == c.dataqsiz {c.sendx = 0}c.qcount++unlock(&c.lock)return true}...// 到了这里,说明不考虑接收队列和缓冲区了,那就是要进入发送等待队列了gp := getg()mysg := acquireSudog()mysg.releasetime = 0if t0 != 0 {mysg.releasetime = -1}mysg.elem = epmysg.waitlink = nilmysg.g = gpmysg.isSelect = falsemysg.c = cgp.waiting = mysggp.param = nil// 加到发送等待队列里,然后就进入阻塞状态c.sendq.enqueue(mysg)atomic.Store8(&gp.parkingOnChan, 1)gopark(chanparkcommit, unsafe.Pointer(&c.lock), waitReasonChanSend, traceEvGoBlockSend, 2)KeepAlive(ep)// someone woke us up.if mysg != gp.waiting {throw("G waiting list is corrupted")}gp.waiting = nilgp.activeStackChans = falseclosed := !mysg.successgp.param = nilif mysg.releasetime > 0 {blockevent(mysg.releasetime-t0, 2)}mysg.c = nilreleaseSudog(mysg)// 再检查一下通道是否关闭了,向一个已经关闭的通道发送数据,会 panicif closed {if c.closed == 0 {throw("chansend: spurious wakeup")}panic(plainError("send on closed channel"))}return true
}

这里面关于 closed 和 lock 有一些细节:

  • 在发送开始的时候,我们需要上锁,等数据要么被拿走,要么进入缓冲区了,要么进入等待队列了,再解锁
    • 也就是操作 channel 的发送是需要加锁的
  • 如果一个 channel 被关闭了,此时还向它发送数据,会发生 panic

接收流程

接收数据里,主要涉及的方法是:

func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool)

那么从接收数据的角度来看,要取一个数据,就会出现几种情况

  • 取到数据
    • 从缓冲区取
    • 从等待发送队列里取
  • 取不到数据
    • 进入等待接收队列

在源码里,它的总体流程是:

Created with Raphaël 2.3.0 检查发送等待队列 检查缓冲区 进入接收等待队列
func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) {... lock(&c.lock)// 如果 channel 被关闭if c.closed != 0 {// 如果缓冲区里也没有数据,就直接 return 即可if c.qcount == 0 {... unlock(&c.lock)if ep != nil {typedmemclr(c.elemtype, ep)}return true, false}} else {// 如果 channel 没关闭// 看下等待发送队列里是否有等待协程// 如果有等待发送的协程,那就再去看下缓冲区// 如果没有缓冲区,或是缓冲区没数据,就直接从发送等待队列里拿数据// 如果有缓冲区且有数据,就从缓冲区里拿数据,再把等待队列里的数据追加到缓冲队列的末尾if sg := c.sendq.dequeue(); sg != nil {recv(c, sg, ep, func() { unlock(&c.lock) }, 3)return true, true}}// 到这里就认为不去看接收队列了,直接看缓冲区// 缓冲区有数据,就直接取if c.qcount > 0 {// Receive directly from queueqp := chanbuf(c, c.recvx)if raceenabled {racenotify(c, c.recvx, nil)}if ep != nil {typedmemmove(c.elemtype, ep, qp)}typedmemclr(c.elemtype, qp)c.recvx++if c.recvx == c.dataqsiz {c.recvx = 0}c.qcount--unlock(&c.lock)return true, true}...// 想取数据却没地方可取,那就加入接收等待队列,然后就进入阻塞状态了gp := getg()mysg := acquireSudog()mysg.releasetime = 0if t0 != 0 {mysg.releasetime = -1}mysg.elem = epmysg.waitlink = nilgp.waiting = mysgmysg.g = gpmysg.isSelect = falsemysg.c = cgp.param = nilc.recvq.enqueue(mysg)atomic.Store8(&gp.parkingOnChan, 1)gopark(chanparkcommit, unsafe.Pointer(&c.lock), waitReasonChanReceive, traceEvGoBlockRecv, 2)// someone woke us upif mysg != gp.waiting {throw("G waiting list is corrupted")}gp.waiting = nilgp.activeStackChans = falseif mysg.releasetime > 0 {blockevent(mysg.releasetime-t0, 2)}success := mysg.successgp.param = nilmysg.c = nilreleaseSudog(mysg)return true, success
}

同样的,接收的过程里,也有 closed 和 lock 的一些细节:

  • 在接收过程前,需要加锁,处理完后再解锁
  • 从一个已经关闭的 channel 里取数据,不会造成 panic

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

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

相关文章

腾讯云2核4G轻量服务器5M带宽支持多少人同时在线?

腾讯云轻量2核4G5M带宽服务器支持多少人在线访问&#xff1f;5M带宽下载速度峰值可达640KB/秒&#xff0c;阿腾云以搭建网站为例&#xff0c;假设优化后平均大小为60KB&#xff0c;则5M带宽可支撑10个用户同时在1秒内打开网站&#xff0c;从CPU内存的角度&#xff0c;网站程序效…

从零开始学习:如何使用Selenium和Python进行自动化测试?

安装selenium 打开命令控制符输入&#xff1a;pip install -U selenium 火狐浏览器安装firebug&#xff1a;www.firebug.com&#xff0c;调试所有网站语言&#xff0c;调试功能 Selenium IDE 是嵌入到Firefox 浏览器中的一个插件&#xff0c;实现简单的浏览器操 作的录制与回…

AI:10-基于TensorFlow的玉米病害识别

玉米是世界上最重要的粮食作物之一,然而,玉米病害对其产量和质量造成了严重威胁。传统的病害识别方法通常依赖于人工观察和经验判断,效率低下且易受主观因素影响。近年来,基于深度学习的图像识别技术在农业领域取得了显著进展,为玉米病害的快速、准确识别提供了新的解决方…

干货!SRC漏洞挖掘项目实战经验分享

目录 一、hunter上搜索web.title”nacos”&#xff0c;查找中国境内的资产&#xff0c;定位到两个地址。 二、访问一下8086端口&#xff0c;界面很明显是nacos&#xff0c;直接抓包&#xff0c;创建用户。 三、登录网站&#xff0c;里面看到配置管理。 四、查看下redis.yml…

yolov8剪枝实践

本文使用的剪枝库是torch-pruning &#xff0c;实验了该库的三个剪枝算法GroupNormPruner、BNScalePruner和GrowingRegPruner。 安装使用 安装依赖库 pip install torch-pruning 把 https://github.com/VainF/Torch-Pruning/blob/master/examples/yolov8/yolov8_pruning.py&…

使用wireshark解析ipsec esp包

Ipsec esp包就是ipsec通过ike协议协商好后建立的通信隧道使用的加密包&#xff0c;该加密包里面就是用户的数据&#xff0c;比如通过的语音等。 那么如何将抓出来的esp包解析出来看呢&#xff1f; 获取相关的esp的key信息. 打开wireshark -> edit->preferences 找到pr…

【手写数字识别】GPU训练版本

SVM Adaboost Bagging 完整代码 I import torch import torch.nn.functional as F from torch.utils.data import DataLoader, TensorDataset from torchvision import transforms, datasets import matplotlib.pyplot as plt# 超参数 batch_size 64 num_epochs 10# 数据…

安卓三防平板在行业应用中有哪些优势

在工业维修和检测中&#xff0c;安卓三防平板的应用也十分广泛。它可以搭载各种专业软件和工具&#xff0c;帮助工人们进行设备故障排查和维护&#xff0c;降低了维修成本和停机时间。 一、产品卖点&#xff1a; 1. 防水性能&#xff1a;该手持平板采用了防水设计&#xff0c;…

国标28181 开源WVP-PRO项目部署

感谢大牛的开源框架 https://doc.wvp-pro.cn/#/ 一.直接使用源码部署&#xff08;在linux&#xff09; -- 安装环境 yum install -y java-1.8.0-openjdk.x86_64 git maven nodejs npm -- 下载源码-wvp项目 git clone https://gitee.com/pan648540858/wvp-GB28181-pro.git ---…

MyBatis-Plus为简化开发而生

简介 MyBatis-Plus 简称 MP是一个 MyBatis 的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。 他们的愿景是成为 MyBatis 最好的搭档&#xff0c;就像魂斗罗中的 1P、2P&#xff0c;基友搭配&#xff0c;效率翻倍。 特性 无…

【ARM CoreLink 系列 6 -- DMC-400控制器简介】

文章目录 1.1 DMC-400 简介1.1.1 DFI&#xff08;DDR PHY Interface&#xff09;1.1.2 DFI 接口组1.1.3 DMC-400 兼容协议1.1.4 DMC-400 特性1.1.5 DMC-400 Interface 1.1 DMC-400 简介 DMC-400是一个由ARM开发、测试和授权的动态内存控制器&#xff0c;同时 DMC-400也是一个符…

如何实现 Es 全文检索、高亮文本略缩处理

如何实现 Es 全文检索、高亮文本略缩处理 前言技术选型JAVA 常用语法说明全文检索开发高亮开发Es Map 转对象使用核心代码 Trans 接口&#xff08;支持父类属性的复杂映射&#xff09;Trans 接口的不足真实项目落地效果结语 前言 最近手上在做 Es 全文检索的需求&#xff0c;类…

曦力音视频转换工具Xilisoft Video Converter Ultimate mac中文版

Xilisoft Video Converter Ultimate mac是一款功能强大的视频转换软件&#xff0c;它可以将几乎所有流行的视频格式转换为其他格式&#xff0c;包括AVI、MPEG、WMV、DivX、MP4、H.264/AVC、AVCHD、MKV、RM、MOV、XviD、3GP等。此外&#xff0c;它还支持将视频转换为音频格式&am…

选择适合自身业务的HTTP代理有哪些因素决定?

相信对很多爬虫工作者和数据采集的企业来说&#xff0c;如何选购适合自己业务的HTTP代理是一个特别特别困扰的选题&#xff0c;市面上那么多HTTP代理厂商&#xff0c;好像这家有这些缺点&#xff0c;转头又看到另外一家的缺点&#xff0c;要找一家心仪的仿佛大海捞针。今天我们…

前端预览、下载二进制文件流(png、pdf)

前端请求设置 responseType: “blob” 后台接口返回的文件流如下&#xff1a; 拿到后端返回的文件流后&#xff1a; 预览 <iframe :src"previewUrl" frameborder"0" style"width: 500px; height: 500px;"></iframe>1、预览 v…

虹科分享 | 想买车无忧?AR为您带来全新体验!

新能源汽车的蓬勃发展&#xff0c;推动着汽车行业加速进行数字化变革。据数据显示&#xff0c;全球新能源汽车销售额持续上升&#xff0c;预计到2025年&#xff0c;新能源汽车市场规模将达到约 4200亿美元&#xff0c;年复合增长率超过 30%。这表明消费者对清洁能源出行的需求不…

隔离上网,安全上网

SDC沙盒数据防泄密系统&#xff08;安全上网&#xff0c;隔离上网&#xff09; •深信达SDC沙盒数据防泄密系统&#xff0c;是专门针对敏感数据进行防泄密保护的系统&#xff0c;根据隔离上网和安全上网的原则实现数据的代码级保护&#xff0c;不会影响工作效率&#xff0c;不…

数据挖掘与统计分析——T检验,正态性检验和一致性检验——代码复现

T检验是一种统计测试&#xff0c;用于确定两个样本组的均值是否有统计学上的显著差异。以下是对T检验的详细介绍&#xff1a; 定义&#xff1a; T检验是一种参数检验&#xff0c;它的前提是数据近似于正态分布。它通过计算T统计量&#xff0c;并将其与特定分布&#xff08;T分…

PyTorch Lightning - LightningModule 训练逻辑 (training_step) 异常处理 try-except

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/133673820 在使用 LightningModule 框架训练模型时&#xff0c;因数据导致的训练错误&#xff0c;严重影响训练稳定性&#xff0c;因此需要使用 t…

华为OD机试 - 数组组成的最小数字(Java 2023 B卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#…