Golang的channel
目录
基本使用
channel 数据结构
阻塞的协程队列
协程节点
构建 channel
写流程
读流程
非阻塞与阻塞
closechan(关闭)
基本使用
创建无缓存 channel
c := make(chan int) //创建无缓冲的通道 cc := make(chan int,0) //创建无缓冲的通道 c
创建有缓存 channel
c := make(chan int, 3) //创建无缓冲的通道 c

例子:
package mainimport ("fmt""time"
)func main() {c := make(chan int, 3) //创建有缓冲的通道 c//内置函数 len 返回未被读取的缓冲元素数量,cap 返回缓冲区大小fmt.Printf("len(c)=%d, cap(c)=%d\n", len(c), cap(c))go func() {defer fmt.Println("子go程结束")for i := 0; i < 3; i++ {c <- ifmt.Printf("子go程正在运行[%d]: len(c)=%d, cap(c)=%d\n", i, len(c), cap(c))}}()time.Sleep(2 * time.Second) //延时2sfor i := 0; i < 3; i++ {num := <-c //从c中接收数据,并赋值给numfmt.Println("num = ", num)}fmt.Println("main进程结束")
}
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 uint16closed uint32elemtype *_type // element typesendx uint // send indexrecvx uint // receive indexrecvq waitq // list of recv waiterssendq waitq // list of send waiterslock mutex
}

hchan:channel 数据结构
• qcount:当前 channel 中存在多少个元素;
• dataqsize: 当前 channel 能存放的元素容量;
• buf:channel 中用于存放元素的环形缓冲区;
• elemsize:channel 元素类型的大小;
• closed:标识 channel 是否关闭;
• elemtype:channel 元素类型;
• sendx:发送元素进入环形缓冲区的 index;
• recvx:接收元素所处的环形缓冲区的 index;
• recvq:因接收而陷入阻塞的协程队列;
• sendq:因发送而陷入阻塞的协程队列;
lock mutex 锁
阻塞的协程队列
type waitq struct {first *sudoglast *sudog
}
waitq:阻塞的协程队列
• first:队列头部
• last:队列尾部
协程节点
sudog:用于包装协程的节点
type sudog struct {g *gnext *sudogprev *sudogelem unsafe.Pointer // data element (may point to stack)isSelect boolc *hchan
}

• g:goroutine,协程;
• next:队列中的下一个节点;
• prev:队列中的前一个节点;
• elem: 读取/写入 channel 的数据的容器;
• isSelect:标识当前协程是否处在 select 多路复用的流程中;
• c:标识与当前 sudog 交互的 chan.
构建 channel
func makechan(t *chantype, size int) *hchan {elem := t.elem// ...mem, overflow := math.MulUintptr(elem.size, uintptr(size))if overflow || mem > maxAlloc-hchanSize || size < 0 {panic(plainError("makechan: size out of range"))}var c *hchanswitch {case mem == 0:// Queue or element size is zero.c = (*hchan)(mallocgc(hchanSize, nil, true))// Race detector uses this location for synchronization.c.buf = c.raceaddr()case elem.ptrdata == 0:// Elements do not contain pointers.// Allocate hchan and buf in one call.c = (*hchan)(mallocgc(hchanSize+mem, nil, true))c.buf = add(unsafe.Pointer(c), hchanSize)default:// Elements contain pointers.c = new(hchan)c.buf = mallocgc(mem, elem, true)}c.elemsize = uint16(elem.size)c.elemtype = elemc.dataqsiz = uint(size)lockInit(&c.lock, lockRankHchan)return
}

写流程
func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool {// ...//加锁lock(&c.lock)// ...//写时存在阻塞读协程if sg := c.recvq.dequeue(); sg != nil {// Found a waiting receiver. We pass the value we want to send// directly to the receiver, bypassing the channel buffer (if any).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)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.elem = epmysg.g = gpmysg.c = cgp.waiting = mysgc.sendq.enqueue(mysg)atomic.Store8(&gp.parkingOnChan, 1)gopark(chanparkcommit, unsafe.Pointer(&c.lock), waitReasonChanSend, traceEvGoBlockSend, 2)gp.waiting = nilclosed := !mysg.successgp.param = nilmysg.c = nilreleaseSudog(mysg)return true
}

总结:
1.当写时存在阻塞读协程,我们直接用
2.当写时不存在阻塞读协程,且缓冲区不满仍有空间时,我们直接加入环形缓冲区中
3.当写时不存在阻塞读协程,且缓冲区满了没用空间时,加入阻塞写协程队列中
注意:
1.有阻塞读协程和缓冲区满之间只有一个条件符合
2.对于未初始化的 chan,写入操作会引发死锁
3.对于已关闭的 chan,写入操作会引发 panic.
读流程
读流程与写流程差不多,不同点:
1.加入的是阻塞读队列
2.当环形缓冲区有和无数据时会有不同的操作
注意:
1.读空channel, park挂起,引起死锁
2.channel 已关闭且内部无元素,直接解锁返回
非阻塞与阻塞
区别:
非阻塞模式下,读/写 channel 方法通过一个 bool 型的响应参数,用以标识是否读取/写入成功.
• 所有需要使得当前 goroutine 被挂起的操作,在非阻塞模式下都会返回 false;
• 所有是的当前 goroutine 会进入死锁的操作,在非阻塞模式下都会返回 false;
• 所有能立即完成读取/写入操作的条件下,非阻塞模式下会返回 true.
何时进入非阻塞
默认情况下,读/写 channel 都是阻塞模式,只有在 select 语句组成的多路复用分支中,
与 channel 的交互会变成非阻塞模式:
在sudog:用于包装协程的节点
• isSelect:标识当前协程是否处在 select 多路复用的流程中;
closechan(关闭)
func closechan(c *hchan) {if c == nil {panic(plainError("close of nil channel"))}lock(&c.lock)if c.closed != 0 {unlock(&c.lock)panic(plainError("close of closed channel"))}c.closed = 1var glist gList// release all readersfor {sg := c.recvq.dequeue()if sg == nil {break}if sg.elem != nil {typedmemclr(c.elemtype, sg.elem)sg.elem = nil}gp := sg.ggp.param = unsafe.Pointer(sg)sg.success = falseglist.push(gp)}// release all writers (they will panic)for {sg := c.sendq.dequeue()if sg == nil {break}sg.elem = nilgp := sg.ggp.param = unsafe.Pointer(sg)sg.success = falseglist.push(gp)}unlock(&c.lock)// Ready all Gs now that we've dropped the channel lock.for !glist.empty() {gp := glist.pop()gp.schedlink = 0goready(gp, 3)
关闭未初始化过的 channel 会 panic;
• 加锁;
• 重复关闭 channel 会 panic;
• 将阻塞读协程队列中的协程节点统一添加到 glist;
• 将阻塞写协程队列中的协程节点统一添加到 glist;
• 唤醒 glist 当中的所有协程.
防止还有协程挂起,没有被唤醒的资源浪费
参考:小徐先生1212 -- Golang Channel实现原理
相关文章:
Golang的channel
目录 基本使用 channel 数据结构 阻塞的协程队列 协程节点 构建 channel 写流程 读流程 非阻塞与阻塞 closechan(关闭) 基本使用 创建无缓存 channel c : make(chan int) //创建无缓冲的通道 cc : make(chan int,0) //创建无缓冲的通道 c 创建有缓存 channel c : m…...
DIYGW可视化开发工具:微信小程序与多端应用开发的利器
一、引言 随着移动互联网的飞速发展,微信小程序以其轻便、易用和跨平台的特点受到了广泛关注。然而,微信小程序的开发相较于传统的H5网页开发,在UI搭建和交互设计上存在一定的挑战。为了应对这些挑战,开发者们一直在寻找更加高效…...
docker——基础知识
简介 一、什么是虚拟化和容器化 实体计算机叫做物理机,有时也称为寄主机; 虚拟化:将一台计算机虚拟化为多台逻辑计算机; 容器化:一种虚拟化技术,操作系统的虚拟化;将用户空间软件实…...
SAP MMRV/MMPV 物料账期月结月底月初开关
公告:周一至周五每日一更,周六日存稿,请您点“关注”和“在看”,后续推送的时候不至于看不到每日更新内容,感谢。 这是一条刮刮乐,按住全部选中:点关注的人最帅最美,欢迎࿱…...
五分钟看懂如何解决FP独立站的广告投放问题
在数字化时代的浪潮中,跨境电商的独立站成为了商家们的新宠。与传统的电商平台相比,独立站在品牌建设、市场定位以及客户体验上提供了更多的自由度和创新空间。然而,这些独立站尤其是销售FP产品的站点,在广告投放上遇到了重重障碍…...
学习分享-FutureTask
前言 今天再改简历的时候回顾了之前实习用到的FutureTask,借此来回顾一下相关知识。 FutureTask 介绍 FutureTask 是 Java 并发包(java.util.concurrent)中的一个类,用于封装异步任务。它实现了 RunnableFuture 接口࿰…...
Javaweb02-XML概述
第一章 XML概述 1.XML基本概念 什么是xml? **a.**引入的原因:为了解决不同不同语言之间的数据传输的格式不同 **b.**概念:XML是一种可扩展标记语言,适用于不同数据之间的数据交换 **c.**XML文档:通过元素的嵌套&a…...
Linux shell编程基础
Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问 Linux 内核的服务。 Shell 脚本&#x…...
2024.6.12 作业 xyt
今日课堂练习:vector构造函数 #include <iostream> #include <vector> using namespace std;void printVector(vector<int> &v) {vector<int>::iterator iter;for(iterv.begin(); iter ! v.end(); iter){cout << *iter <<…...
QTTabBar在重置Internet Explorer后失效
网上常见的办法是: 打开IE浏览器>>设置>>Internet选项>>高级。勾选启用第三方浏览器扩展,重启后生效。 打开IE浏览器-设置–管理加载项,启用QTTabBar。 实际在Win10上使用的时候会遇到点开IE自动跳转到Edge的问题。这时…...
Django之云存储(一)
一、介绍 用户上传的文件以及项目中使用的静态文件,除了保存在本地服务器,还在可以保存在云服务中,比如: 阿里云七牛云(课程选用)亚马逊云等1.1、使用方式 注册账号 七牛云开发者平台 实名认证 创建空间...
推挽与开漏输出
一般来说,微控制器的引脚都会有一个驱动电路,可以配置不同类型的数字和模拟电路接口。输出模式一般会有推挽与开漏输出。 推挽输出 推挽输出(Push-Pull Output),故名思意能输出两种电平,一种是推…...
Sora和快手可灵背后的核心技术 | 3DVAE:通过小批量特征交换实现身体和面部的三维形状变分自动编码器
【摘要】学习3D脸部和身体生成模型中一个解开的、可解释的和结构化的潜在表示仍然是一个开放的问题。当需要控制身份特征时,这个问题尤其突出。在本文中,论文提出了一种直观而有效的自监督方法来训练一个3D形状变分自动编码器(VAE),以鼓励身份特征的解开潜在表示。通过交换不同…...
ArcGIS Pro SDK (三)Addin控件 2 窗格界面类
ArcGIS Pro SDK (三)Addin控件 2 窗格界面类 目录 ArcGIS Pro SDK (三)Addin控件 2 窗格界面类15 ArcGIS Pro 后台选项卡15.1 添加控件15.2 Code15.2.1 选项卡按钮15.2.2 选项卡页 16 ArcGIS Pro 窗体16.1 添加控件16.2 Code 17 A…...
Ubuntu 20.04.6 LTS系统使用命令编辑静态IP地址【笔记】
rootubuntu-machine:/home# cat /etc/issue Ubuntu 20.04.6 LTS \n \l1、切换到root身份 sudo su2、编辑静态IP地址,示例以01-network-manager-all.yaml,个别系统可能是00-network-manager-all.yaml,以安装系统生成的文件为准。 vim /etc/n…...
Python第二语言(八、Python包)
目录 1. 什么是Python包 2. 创包步骤 2.1 new包 2.2 查看创建的包 2.3 拖动文件到包下 3. 导入包 4. 安装第三方包 4.1 什么是第三方包 4.2 安装第三方包-pip 4.3 pip网络优化 1. 什么是Python包 包下有__init__.py就是包,无__init__.py就是文件夹。于Ja…...
Pipeline流水线组件
文章目录 1、新建pipeline流水线2、定义处理器3、定义处理器上下文4、pipeline流水线实现5、处理器抽象类实现6、pipeline流水线构建者7、具体处理器实现8、流水线测试9、运行结果 1、新建pipeline流水线 package com.summer.toolkit.model.chain;import java.util.List; impo…...
闪灵CMS电子商城系统源码v5.0(自带微信小程序)
源码介绍 闪灵CMS电子商城系统源码,双语带手机版,PHPMYSQL进行开发,网站安装简单、快捷。 闪灵CMS系统更新日志 1.修复:修复了开启强制https后,说明文档重定向过多的问题 2.修复:修复了商品名称过长时无…...
基于SSM的旅游民宿预定系统【源码】【运行教程】
基于SSM的旅游民宿预定系统 一、项目介绍1. 游客功能2. 管理员功能3. 高级功能 二、项目技术栈三、项目运行四、项目演示总结 大家好,这里是程序猿代码之路!随着旅游业的快速发展,民宿作为一种独特的住宿方式越来越受到游客的喜爱。为了提升用…...
PgSQL技术内幕 - psql与服务端连接与交互机制
PgSQL技术内幕 - 客户端psql与服务端连接与交互机制 简单来说,PgSQL的psql客户端向服务端发起连接请求,服务端接收到请求后,fork出一个子进程,之后由该子进程和客户端进行交互,处理客户端的SQL等,并将结果返…...
树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...
【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...
网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...
