当前位置: 首页 > news >正文

golang深刻剖析——channel

文章目录

    • 1 概念
    • 2 分类
    • 3 操作
      • 3.1 channel 的创建
        • 3.1.1 无缓冲channel
        • 3.1.1 带缓冲channel
      • 3.2 channel的读写
      • 3.3 channel的关闭
      • 3.4 channel 和 select
    • 4 channel 底层原理

1 概念

channel 是一个通道,用于端到端的数据传输,这有点像我们平常使用的消息队列,只不过 channel 的发送方和接受方是 goroutine 对象,属于内存级别的通信。

2 分类

3 操作

在深入了解 channel 的底层之前,我们先来看看 channel 的常用用法。

3.1 channel 的创建

3.1.1 无缓冲channel

 ch := make(chan int)

对于无缓冲的 channel,一旦有 goroutine 往 channel 写入数据,那么当前的 goroutine 会被阻塞住,直到有其他的 goroutine 消费了 channel 里的数据,才能继续运行写入数据。

3.1.1 带缓冲channel

还有另外一种是有缓冲的 channel,它的创建是这样的:

ch := make(chan int, 10)

其中,第二个参数表示 channel 可缓冲数据的容量。只要当前 channel 里的元素总数不大于这个可缓冲容量,则当前的 goroutine 就不会被阻塞住。

另外,我们也可以声明一个 nil 的 channel,只是创建这样的 channel 没有意义,读、写 channel 都将会被阻塞住。一般 nil channel 用在 select 上,让 select 不再从这个 channel 里读取数据,如下用法:

ch1 := make(chan int)
ch2 := make(chan int)
go func() {if !ok { // 某些原因,设置 ch1 为 nilch1 = nil}}()
for {select {case <-ch1: // 当 ch1 被设置为 nil 后,将不会到达此分支了。doSomething1()case <-ch2:doSomething2()}
}

3.2 channel的读写

3.3 channel的关闭

ch := make(chan int,10)...
close(ch)

面试题:当关闭channel之和再操作channel会发生什么?

写数据:则程序会直接 panic 退出;
读数据:(1) 有数据:读到关闭之前写入的数据;
    (2) 无数据:将得到零值,即对应类型的默认值。

3.4 channel 和 select

在写程序时,有时并不单单只会和一个 goroutine 通信,当我们要进行多 goroutine 通信时,则会使用 select 写法来管理多个 channel 的通信数据:

 ch1 := make(chan struct{})ch2 := make(chan struct{})// ch1, ch2 发送数据go sendCh1(ch1)go sendCh1(ch2)// channel 数据接受处理for {select {case <-ch1:doSomething1()case <-ch2:doSomething2()}}

channel 的死锁
前面提到过,往 channel 里读写数据时是有可能被阻塞住的,一旦被阻塞,则需要其他的 goroutine 执行对应的读写操作,才能解除阻塞状态。

然而,阻塞后一直没能发生调度行为,没有可用的 goroutine 可执行,则会一直卡在这个地方,程序就失去执行意义了。此时 Go 就会报 deadlock 错误,如下代码:

 func main() {ch := make(chan int)<-ch// 执行后将 panic:// fatal error: all goroutines are asleep - deadlock!}

因此,在使用 channel 时要注意 goroutine 的一发一取,避免 goroutine 永久阻塞!

4 channel 底层原理

前面提及过 channel 创建后返回了 hchan 结构体,现在我们来研究下这个结构体,它的主要字段如下:

type hchan struct {//channel分为无缓冲和有缓冲两种。//对于有缓冲的channel存储数据,借助的是如下循环队列的结构qcount   uint           // 循环队列中的元素数量dataqsiz uint           // 循环队列的长度buf      unsafe.Pointer // 指向底层循环队列的指针elemsize uint16         //能够收发元素的大小closed   uint32        //channel是否关闭的标志elemtype *_type        //channel中的元素类型//有缓冲channel内的缓冲数组会被作为一个“环型”来使用。//当下标超过数组容量后会回到第一个位置,所以需要有两个字段记录当前读和写的下标位置sendx    uint   // 下一次发送数据的下标位置recvx    uint   // 下一次读取数据的下标位置//当循环数组中没有数据时,收到了接收请求,那么接收数据的变量地址将会写入读等待队列//当循环数组中数据已满时,收到了发送请求,那么发送数据的变量地址将写入写等待队列recvq    waitq  // 读等待队列sendq    waitq  // 写等待队列lock mutex //互斥锁,保证读写channel时不存在并发竞争问题
}

channel 在进行读写数据时,会根据无缓冲、有缓冲设置进行对应的阻塞唤起动作,它们之间还是有区别的。下面我们来捋一下这些不同之处。

无缓冲 channel
由于对 channel 的读写先后顺序不同,处理也会有所不同,所以,还得再进一步区分:

channel 先写再读
在这里,我们暂时认为有 2 个 goroutine 在使用 channel 通信,按先写再读的顺序,则具体流程如下:

可以看到,由于 channel 是无缓冲的,所以 G1 暂时被挂在 sendq 队列里,然后 G1 调用了 gopark 休眠了起来。

接着,又有 goroutine 来 channel 读取数据了:

此时 G2 发现 sendq 等待队列里有 goroutine 存在,于是直接从 G1 copy 数据过来,并且会对 G1 设置 goready 函数,这样下次调度发生时, G1 就可以继续运行,并且会从等待队列里移除掉。

channel 先读再写
先读再写的流程跟上面一样。

G1 暂时被挂在了 recvq 队列,然后休眠起来。

G2 在写数据时,发现 recvq 队列有 goroutine 存在,于是直接将数据发送给 G1。同时设置 G1 goready 函数,等待下次调度运行。

有缓冲 channel
在分析完了无缓冲 channel 的读写后,我们继续看看有缓冲 channel 的读写。同样的,我们分为 2 种情况:

channel 先写再读
这一次会优先判断缓冲数据区域是否已满,如果未满,则将数据保存在缓冲数据区域,即环形队列里。如果已满,则和之前的流程是一样的。

当 G2 要读取数据时,会优先从缓冲数据区域去读取,并且在读取完后,会检查 sendq 队列,如果 goroutine 有等待队列,则会将它上面的 data 补充到缓冲数据区域,并且也对其设置 goready 函数。

channel 先读再写
此种情况和无缓冲的先读再写是一样流程,此处不再重复说明。

四、总结
有缓冲 channel 和无缓冲 channel 的读写基本相差不大,只是多了缓冲数据区域的判断而已。

channel 在使用的时候大多时候得和 select 配合使用,尽管只需要简单的用 <- ch 和 ch <- 来读写数据,但它的底层还是很有讲究的,特别是涉及到调度的休眠唤起。

这也能看出 Go 的精妙之处:复杂底层,优雅运用。

相关文章:

golang深刻剖析——channel

文章目录 1 概念2 分类3 操作3.1 channel 的创建3.1.1 无缓冲channel3.1.1 带缓冲channel 3.2 channel的读写3.3 channel的关闭3.4 channel 和 select 4 channel 底层原理 1 概念 channel 是一个通道&#xff0c;用于端到端的数据传输&#xff0c;这有点像我们平常使用的消息队…...

ERROR in unable to locate ‘***/public/**/*‘ glob

前提 自己搭了一个react项目的脚手架&#xff0c;npm包下载一切都很正常&#xff0c;启动的时候突然就报ERROR in unable to locate ***/public/**/* glob这个错误&#xff0c;根据百度分析了一下产生的原因&#xff1a;webpack配置文件中的CopyWebpackPlugin导致的 网上给出的…...

简述一下你了解的 Java 设计模式

创建型模式 ★单例模式&#xff1a;保证某个类只能有一个唯一实例&#xff0c;并提供一个全局的访问点。 ★简单工厂&#xff1a;一个工厂类根据传入的参数决定创建出那一种产品类的实例。 工厂方法&#xff1a;定义一个创建对象的接口&#xff0c;让子类决定实例化那个类。 抽…...

[开发] 认证的几种方式简介

LDAP 认证 LDAP&#xff08;轻量级目录访问协议&#xff09;是一种用于访问和维护分布式目录信息的开放标准协议。它最初由电子数据系统公司&#xff08;Netscape&#xff09;开发&#xff0c;现在被广泛用于企业和组织中的身份认证和授权管理。LDAP的目标是为不同类型的应用程…...

ansible-playbook roles模块编写lnmp剧本

目录 一&#xff1a;集中式编写lnmp剧本 二&#xff1a;分布式安装lnmp 1、nginx 配置 2、mysql配置 3、php配置 4、运行剧本 一&#xff1a;集中式编写lnmp剧本 vim /etc/ansible/lnmp.yml- name: lnmp playhosts: dbserversremote_user: roottasks:- name: perpare condif…...

什么是汽车软件的模糊测试?

汽车软件的模糊测试&#xff08;fuzz testing&#xff09;是一种在软件开发过程中用来发现潜在漏洞和缺陷的测试方法。它通过输入大量的随机、无效或异常数据来模拟真实环境中各种可能的异常情况&#xff0c;以测试软件的健壮性和稳定性。 1. 确定模糊测试的目标&#xff1a;确…...

Datax使用

参考文档 datax 安装包 安装包 安装java sudo yum install java-1.8.0-openjdk sudo yum install java-1.8.0-openjdk-develvim /etc/profileexport JAVA_HOME/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.372.b07-1.el7_9.x86_64 export PATH$JAVA_HOME/bin:$PATHsource /etc…...

HTML不常用但是好用的标签

sub sup <p>这个文本包含 <sub>111</sub>文本。</p> <p>这个文本包含 <sup>上标</sup> 文本。</p>下标文本将会显示在当前文本流中字符高度的一半为基准线的下方&#xff0c;但是与当前文本流中文字的字体和字号都是一样的。…...

蓝桥杯2018省赛全球变暖dfs

全球变暖 问题描述格式输入格式输出样例输入样例输出评测用例规模与约定解析参考程序 问题描述 格式输入 格式输出 输出一个整数 样例输入 样例输出 1 评测用例规模与约定 最大运行时间&#xff1a;1s最大运行内存: 256M 解析 采用dfs的方式进行搜索&#xff0c;首先输入地…...

Bean的作用域 - spring

前言 本篇介绍Bean的6种作用域&#xff0c;与通过注释修改作用域的方式&#xff0c;如有错误&#xff0c;请在评论区指正&#xff0c;让我们一起交流&#xff0c;共同进步&#xff01; 文章目录 前言1. Bean的作用域1.1 singleton - 单例模式 - 默认作用域1.2 prototype - 原型…...

[ncnn]ncnnoptimize使用

usage: ncnnoptimize [inparam] [inbin] [outparam] [outbin] [flag] [cutstart] [cutend] 使用案例&#xff1a; ./ncnnoptimize yolov5s_6.2.param yolov5s_6.2.bin yolov5s_6.2_opt.param yolov5s_6.2_opt.bin 65536ncnnoptimize [原模型param文件] [原模型bin文件] [新模…...

Elasticsearch笔记

迈向光明之路&#xff0c;必定荆棘丛生。 文章目录 一、Elasticsearch概述二、初识ES倒排索引1. 正向索引2. 倒排索引 三、ES环境搭建1. 安装单机版ES2. 安装Kibana3. 安装ik分词器3.1 在线安装ik插件3.2.离线安装ik插件&#xff08;推荐方式&#xff09;3.3 自定义词典 四、ES…...

《怎样顺利通过答辩:论文答辩的策略与技巧》

最近在阅读《怎样顺利通过答辩这本书》&#xff0c;记录一下阅读获取的关键信息和心得。 目录 第一章 答辩是什么 在答辩前你需要做到以下几件事情&#xff0c;核查清单如下&#xff1a; 答辩根据考生及其研究的质量&#xff0c;服务于不同的目的&#xff1a; 通常意义上的…...

uniapp 微信小程序:页面+组件的生命周期顺序

uniapp 微信小程序&#xff1a;页面组件的生命周期顺序 首页页面父组件子组件完整顺序参考资料 这个uniapp的微信小程序项目使用的是 VUE2 首页 首页只提供了一个跳转按钮。 <template><view><navigator url"/pages/myPage/myPage?namejerry" hov…...

Linux CentOS 8 编译安装Apache Subversion

前言 距离上一篇发表已经过去了5年零2个多月&#xff0c;这次重新开始写技术博客&#xff0c;理由和原来一样&#xff0c;也就是想把自己学习和工作中遇到的问题和知识记录下来&#xff0c;今天记录一下Linux CentOS 8通过编译安装svn的过程。 下载SVN 下载地址&#xff1a;…...

谈一谈缓存穿透,击穿,雪崩

缓存穿透 缓存穿透是指在使用缓存系统时&#xff0c;频繁查询一个不存在于缓存中的数据&#xff0c;导致这个查询每次都要通过缓存层去查询数据源&#xff0c;无法从缓存中获得结果。这种情况下&#xff0c;大量的请求会直接穿透缓存层&#xff0c;直接访问数据源&#xff0c;…...

如何对反编译的安卓应用进行调试并修改

安卓修改大师可以在没有源代码的情况下&#xff0c;直接反编译已经打包的APK安装包&#xff0c;通过修改SMALI代码实现添加和去除部分功能&#xff0c;并在应用的任何地方添加任意代码&#xff0c;增加任意任何您想实现的功能。通过这种方式&#xff0c;把该应用变为您自己的应…...

C#实现数据库数据变化监测(sqlservermysql)

监测数据库表数据变化&#xff0c;可实现数据库同步&#xff08;一主一从&#xff08;双机备份&#xff09;&#xff0c;一主多从&#xff08;总部数据库&#xff0c;工厂1&#xff0c;工厂2&#xff0c;工厂数据合并到总部数据&#xff09;&#xff09; sqlserver 启用数据库…...

MFC第二十三天 HBrush对闭合图形的填充、CPen、CFont类常用功能与LOGFONT和LOGPEN结构体

文章目录 HBrush对闭合图形的填充HBITMAP位图资源的加载和平铺填充CFont类常用功能与LOGFONT结构体CPen类简介 HBrush对闭合图形的填充 HBRUSH创建&#xff1a; a)实色填充&#xff1a; HBRUSH CreateSolidBrush( COLORREF color);b)栅格线填充&#xff1a; HBRUSH CreateHa…...

深入学习 Redis - 渐进式遍历 scan 命令、数据库管理命令

目录 前言 一、scan 命令 二、数据库管理命令 select dbsize flushdb / flushall 前言 之前我们所了解到的 keys * 是一次性把整个 redis 中所有的 key 都获取到&#xff0c;但是整个操作比较危险&#xff0c;可能会一下子的都太多的 key&#xff0c;阻塞 redis 服务器. …...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分&#xff1a; 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析&#xff1a; CTR…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

智慧医疗能源事业线深度画像分析(上)

引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

Leetcode 3577. Count the Number of Computer Unlocking Permutations

Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接&#xff1a;3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯&#xff0c;要想要能够将所有的电脑解锁&#x…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

多种风格导航菜单 HTML 实现(附源码)

下面我将为您展示 6 种不同风格的导航菜单实现&#xff0c;每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

Angular微前端架构:Module Federation + ngx-build-plus (Webpack)

以下是一个完整的 Angular 微前端示例&#xff0c;其中使用的是 Module Federation 和 npx-build-plus 实现了主应用&#xff08;Shell&#xff09;与子应用&#xff08;Remote&#xff09;的集成。 &#x1f6e0;️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)

推荐 github 项目:GeminiImageApp(图片生成方向&#xff0c;可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...

Linux 中如何提取压缩文件 ?

Linux 是一种流行的开源操作系统&#xff0c;它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间&#xff0c;使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的&#xff0c;要在 …...