Golang的Channel源码阅读、工作流程分析。

Channel整体结构

源码位置

位于src/runtime下的chan.go中。

在这里插入图片描述

Channel整体结构图

图源:https://i6448038.github.io/2019/04/11/go-channel/

在这里插入图片描述

Channel结构体

type hchan struct {
	qcount   uint           // total data in the queue
	dataqsiz uint           // size of the circular queue
	buf      unsafe.Pointer // points to an array of dataqsiz elements
	elemsize uint16
	closed   uint32
	elemtype *_type // element type
	sendx    uint   // send index
	recvx    uint   // receive index
	recvq    waitq  // list of recv waiters
	sendq    waitq  // list of send waiters

	// lock protects all fields in hchan, as well as several
	// fields in sudogs blocked on this channel.
	//
	// Do not change another G's status while holding this lock
	// (in particular, do not ready a G), as this can deadlock
	// with stack shrinking.
	lock mutex
}

我们可以看到,其中有一个buf空间,这个对应的是我们生成的有缓冲通道、无缓冲通道。recvqsendq对应的是waitq类型,其中主要存储的是发送、接受方的Goroutine。

waitq&&sudog

waitq


type waitq struct {
	first *sudog
	last  *sudog
}

sudog

// sudogs are allocated from a special pool. Use acquireSudog and
// releaseSudog to allocate and free them.
type sudog struct {
	// The following fields are protected by the hchan.lock of the
	// channel this sudog is blocking on. shrinkstack depends on
	// this for sudogs involved in channel ops.

	g *g

	next *sudog
	prev *sudog
	elem unsafe.Pointer // data element (may point to stack)

	// The following fields are never accessed concurrently.
	// For channels, waitlink is only accessed by g.
	// For semaphores, all fields (including the ones above)
	// are only accessed when holding a semaRoot lock.

	acquiretime int64
	releasetime int64
	ticket      uint32

	// isSelect indicates g is participating in a select, so
	// g.selectDone must be CAS'd to win the wake-up race.
	isSelect bool

	// success indicates whether communication over channel c
	// succeeded. It is true if the goroutine was awoken because a
	// value was delivered over channel c, and false if awoken
	// because c was closed.
	success bool

	parent   *sudog // semaRoot binary tree
	waitlink *sudog // g.waiting list or semaRoot
	waittail *sudog // semaRoot
	c        *hchan // channel
}

Channel工作流程

创建管道

先在创建阶段:会根据缓冲大小对buf进行初始化,无缓冲通道的buf为0。具体见

发送数据

发送数据前:
首先会进行加锁(因此-“一个通道同时只能进行一个收/发操作”)。如果Channel已关闭,则会报panic。

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

	if c.closed != 0 {
		unlock(&c.lock)
		panic(plainError("send on closed channel"))
	}

发送数据时,会分为多种情况:
1、有等待的接收者——直接发给阻塞的接收者。
2、无等待 但是缓冲区有空间——写入Channel的缓冲区。
3、无等待 无空间——等待其他Goroutine接受数据。

update:刚才脑子转不过来了一下。。疑惑:情况1的时候,那缓存内的东西先进去,不应该排队放后面吗?为什么直接丢给goroutine了。一下反应过来:如果缓存有内容,那接收者就直接拿了啊!!阻塞,就说明他已经缓存拿不到东西,才会去阻塞等待的。

工作流程:(由于GPT4.0解读源码总结完成)

1、检查通道是否为nil:如果尝试向一个nil的通道发送数据,如果是非阻塞的(block为false),则直接返回false;如果是阻塞的,则该goroutine会被挂起,直到被唤醒(实际上,向nil通道发送数据会导致永久阻塞,这里的唤醒仅是理论上的,因为后面紧接着会调用throw("unreachable")抛出异常,表示这个代码路径不应该被执行)。

2、快速路径检查:在尝试获取锁之前,先检查通道是否已关闭并且是否已满,以避免在这些明显无法发送成功的情况下还获取锁,提高效率。

3、获取锁:为了保证对通道状态的修改是安全的,需要先获取通道的锁。

4、检查通道是否已关闭:如果通道已经关闭,则抛出“send on closed channel”的异常。

5、尝试直接发送给等待接收的goroutine:如果有goroutine正在等待接收(即接收队列不为空),则直接将值传递给它,并唤醒该goroutine。

6、检查通道缓冲区是否有空间:如果通道的缓冲区还有空间,则将值放入缓冲区,并更新相关指标。

7、非阻塞发送失败:如果是非阻塞发送且到达这一步,说明无法立即发送,释放锁并返回false8、准备阻塞发送:如果是阻塞发送,则创建一个sudog对象表示当前goroutine,将其加入到发送队列中,并挂起当前goroutine等待被唤醒(通常是接收方接收到值或通道被关闭时唤醒)。

9、唤醒后的处理:被唤醒后,检查发送是否成功(通过检查sudog的success字段)。如果通道在等待期间被关闭,则抛出“send on closed channel”的异常。

10、资源清理和返回:最后,释放sudog资源,返回发送是否成功。

详细源码工作流程,见此

接收数据

当已被关闭&&缓冲区没有数据,会返回。

接收的三种情况:
1、存在发送者时,直接从发送者或缓冲区数据。
2、缓冲区存在数据,从缓冲区接收。
3、都不存在时,等待其他Goroutine发送。

源码阅读(chanrecv函数):

1、检查通道是否为空:如果尝试从一个nil的通道接收数据,根据block参数的不同,可能会导致goroutine挂起或者直接返回。

2、快速路径检查:在不阻塞的情况下,如果通道为空,则尝试检查通道是否关闭。如果通道已关闭且为空,则清空指针ep指向的内存(如果ep不为nil)并返回。

3、加锁:为了修改通道状态,需要先获取通道的锁。

4、通道已关闭且无数据:如果通道已关闭并且没有数据,清空ep指向的内存并返回。

5、从等待发送的goroutine接收数据:如果通道未关闭且有等待发送的goroutine,直接从发送方接收数据。

6、从通道缓冲区接收数据:如果通道有数据(qcount > 0),则从通道的缓冲区接收数据到ep指向的位置,并清空缓冲区中该数据的位置。

7、非阻塞情况下无数据可接:如果是非阻塞接收且到达这一步,说明无法立即接收数据,释放锁并返回。

8、准备阻塞接收:如果是阻塞接收,则挂起当前goroutine,直到有数据可接收或通道被关闭。

9、唤醒后的处理:被唤醒后,检查接收是否成功。如果接收成功,则ep指向的位置已被填充。

10、资源清理和返回:最后,释放相关资源,返回操作结果。

关闭管道

closechan函数:

1、检查通道是否为nil:如果尝试关闭一个nil的通道,会引发panic2、加锁:为了保证对通道状态的修改是并发安全的,需要先获取通道的锁。

3、检查通道是否已经关闭:如果通道已经被关闭(c.closed != 0),则释放锁并panic。这防止了通道被多次关闭导致的未定义行为。

4、设置通道为关闭状态:将通道的closed标志设置为1,表示该通道已经关闭。

5、处理等待接收的goroutine:遍历接收队列recvq,对于队列中的每个等待接收的goroutine(通过sudog表示),清空它们等待接收的元素指针(如果有),并将它们标记为操作未成功(success = false)。这些goroutine将会被唤醒,但是接收操作会因为通道已关闭而失败。

6、处理等待发送的goroutine:遍历发送队列sendq,对于队列中的每个等待发送的goroutine,清空它们准备发送的元素指针(如果有),并将它们标记为操作未成功。这些goroutine在被唤醒后会感知到通道已经关闭,并可能引发panic7、释放锁:完成上述操作后,释放通道的锁。

8、唤醒所有goroutine:最后,对于通过上述步骤收集到的所有goroutine(存储在glist中),将它们标记为就绪状态(goready),这样它们就可以被调度执行了。这确保了所有因为该通道操作而阻塞的goroutine都能继续执行,无论是因为等待发送还是接收。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python unittest实现api自动化测试

这篇文章主要为大家详细介绍了python unittest实现api自动化测试的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 项目测试对于一个项目的重要性,大家应该都知道吧,写python的朋友,应该都写过自动化测试脚本…

Nginx启动服务

Nginx启动服务 一、启动前置 下载地址 如已安装Docker,下一步拉取Nginx最新的Docker镜像: docker pull nginx:latest查看拉取下来的镜像: docker images二、启动服务 创建Docker容器: docker run --name {projectname} -p 80…

Open3D 生成空间3D椭圆点云

目录 一、算法原理二、代码实现三、结果展示本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT。 一、算法原理 设椭圆在 X O Y XOY XO

MySQL基础-----SQL语句之DCL数据控制语句

目录 前言 一、管理用户 1.查询用户 2.创建用户 3.修改用户密码 4.删除用户 案例 二、权限控制 1.查询权限 2.授予权限 3.撤销权限 案例 前言 本期我们学习SQL语句的最后一部分内容,也就是数据控制语句DCL。DCL英文全称是Data Control Language(数据控制语…

支部管理系统微信小程序(管理端+用户端)flask+vue+mysql+微信小程序

系统架构如图所示 高校D支部管理系统 由web端和微信小程序端组成,由web端负责管理,能够收缴费用、发布信息、发布问卷、发布通知等功能 部分功能页面如图所示 微信小程序端 包含所有源码和远程部署,可作为毕设课设

ctf_show笔记篇(web入门---文件上传)

文件上传 151:简单的前端验证,有多种绕过方法 152:简单后端验证,不知道过滤了那些后缀,我尝试以后都可以上传 153:利用.user.ini文件,虽然能上传.pht这一类文件但访问时只会下载下来 这里就…

本地项目推送到腾讯云轻量应用服务器教程(并实现本地推送远程自动更新)

将本地项目上传到腾讯云轻量应用服务器并实现后续的推送更新,具体步骤如下: 在本地项目目录下初始化 Git 仓库: cd 项目目录 git init将项目文件添加到 Git 仓库并提交: git add . git commit -m "Initial commit"在…

【unity实战】3D水系统,游泳,潜水,钓鱼功能实现

文章目录 素材将项目升级为URP画一个水潭地形材质升级为URP创建水调节水第一人称人物移动控制游泳水面停留添加水下后处理水下呼吸钓鱼参考完结 素材 https://assetstore.unity.com/packages/vfx/shaders/urp-stylized-water-shader-proto-series-187485 将项目升级为URP 这…

在vue3中使用el-tree-select做一个树形下拉选择器

el-tree-select是一个含有下拉菜单的树形选择器,结合了 el-tree 和 el-select 两个组件的功能。 因为包含了el-tree的功能,我们可以自定义tree的节点,创造出想要的组件 使用default插槽可以自定义节点内容,它的default插槽相当于…

理解循环神经网络(RNN)

文章目录 1. 引言:什么是RNN以及它的重要性RNN简介RNN在机器学习中的作用和应用场景 2. RNN的工作原理神经网络基础RNN的结构和运作方式循环单元的作用 3. RNN的关键特点与挑战参数共享长期依赖问题门控机制(例如LSTM和GRU)代码示例&#xff…

【p3128、LQB14I砍树】树上差分

文章目录 差分树上差分p3128LQB14I砍树题目解题步骤代码样例 差分 差分数组求法: 设原始数组是arr,差分数组是b b[0] arr[0];b[i] arr[i] - arr[i-1]; 如果我们要对图中2-4区间的数每个都加上3,就可以在差分数组2的位置加上3,…

三、统计语言模型(N-gram)

为了弥补 One-Hot 独热编码的维度灾难和语义鸿沟以及 BOW 词袋模型丢失词序信息和稀疏性这些缺陷,将词表示成一个低维的实数向量,且相似的词的向量表示是相近的,可以用向量之间的距离来衡量相似度。 N-gram 统计语言模型是用来计算句子概率的…

tomcat基础介绍

目录 一、Tomcat的基本介绍 1、Tomcat是什么? 2、Tomcat的配置文件详解 3、Tomcat的构成组件 6、Tomcat的请求过程 一、Tomcat的基本介绍 1、Tomcat是什么? Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器…

【力扣 - 三数之和】

题目描述 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k ,同时还满足 nums[i] nums[j] nums[k] 0 。请你返回所有和为 0 且不重复的三元组。 注意:答案中不可以包含重复的三元组。…

【C++精简版回顾】21.迭代器(未完成)

1.什么是迭代器&#xff1f; 用来遍历容器&#xff0c;访问容器数据。 2.迭代器使用 1.初始化 //初始化 list<int> mylist;//list的整数对象 list<int>::iterator iter;//list内部类&#xff0c;迭代器对象(正向输出) list<int>::reverse_iterator riter;//…

大数据开发-Hadoop之MapReduce

文章目录 MapReduce原理剖析MapReduce之Map阶段MapReduce之Reduce阶段WordCount分析多文件WordCount分析 实战wordCount案例开发 MapReduce原理剖析 MapReduce是一种分布式计算模型,主要用于搜索领域&#xff0c;解决海量数据的计算问题MapReduce由两个阶段组成&#xff1a;Ma…

论文笔记:Compact Multi-Party Confidential Transactions

https://link.springer.com/chapter/10.1007/978-3-030-65411-5_21 A compact, private, Multi-Party Confidential Transactions (MCT) 紧凑型多方机密交易&#xff08;Compact MCT&#xff09;&#xff1a;MCT的长度与常规的单一所有者交易一样短&#xff1b;换句话说&…

ABAQUS软件报价费用 abaqus正版购买价格多少钱?

ABAQUS软件可以完成哪些模拟&#xff1f; ABAQUS软件是一套功能强大的工程模拟的有限元软件&#xff0c;其解决问题的范围从相对简单的线性分析到许多复杂的非线性问题。ABAQUS软件中包含了一套丰富的单元库&#xff0c;可模拟任意几何形状&#xff1b;还包含了各种类型的材料…

第十四届校模拟赛第一期(一)

“须知少时凌云志&#xff0c;自许人间第一流” 鄙人11月八号有幸参加学校校选拔赛&#xff0c;题型为5道填空题&#xff0c;5道编程题&#xff0c;总时间为4小时。奈何能力有限&#xff0c;只完成了5道填空和3道编程大题&#xff0c;现进行自省自纠&#xff0c;分享学习&#…

【系统安全加固】Centos 设置禁用密码并打开密钥登录

文章目录 一&#xff0c;概述二&#xff0c;操作步骤1. 服务器端生成密钥2. 在服务器上安装公钥3.下载私钥到本地&#xff08;重要&#xff0c;否则后面无法登录&#xff09;4. 修改配置文件&#xff0c;禁用密码并打开密钥登录5. 重启sshd服务6. 配置xshell使用密钥登录 一&am…