Golang Channel 详细原理和使用技巧
1.简介
Channel(一般简写为 chan) 管道提供了一种机制:它在两个并发执行的协程之间进行同步,并通过传递与该管道元素类型相符的值来进行通信,它是Golang在语言层面提供的goroutine间的通信方式.通过Channel在不同的 goroutine中交换数据,在goroutine之间发送和接收消息,并且可以通过Channel实现Go依赖的CSP的并发模型这种同步模式
chan 可以理解为一个管道或者先进先出的队列,Golang并发的核心哲学是:不要通过共享内存来通信,而应该通过通信来共享内存,所以数据在不同协程中的传输都是通过拷贝的形式完成的,并且 channel 本身还可以支持有缓冲和无缓冲的,通过 channel + timeout 实现并发协程之间的同步也是常见的一种使用姿势
2.channel结构体
简单说明:
buf是有缓冲的channel所特有的结构,用来存储缓存数据,是个循环链表sendx和recvx用于记录buf这个循环链表中的发送或者接收的indexlock是个互斥锁recvq和sendq分别是接收(<-channel)或者发送(channel <- xxx)的goroutine抽象出来的结构体(sudog)的队列,是个双向链表
type hchan struct {qcount uint // total data in the queue 当前队列里还剩余元素个数dataqsiz uint // size of the circular queue 环形队列长度,即缓冲区的大小,即make(chan T,N) 中的Nbuf unsafe.Pointer // points to an array of dataqsiz elements 环形队列指针elemsize uint16 //每个元素的大小closed uint32 //标识当前通道是否处于关闭状态,创建通道后,该字段设置0,即打开通道;通道调用close将其设置为1,通道关闭elemtype *_type // element type 元素类型,用于数据传递过程中的赋值sendx uint // send index 环形缓冲区的状态字段,它只是缓冲区的当前索引-支持数组,它可以从中发送数据recvx uint // receive index 环形缓冲区的状态字段,它只是缓冲区当前索引-支持数组,它可以从中接受数据recvq waitq // list of recv waiters 等待读消息的goroutine队列sendq waitq // list of send waiters 等待写消息的goroutine队列// 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 //互斥锁,为每个读写操作锁定通道,因为发送和接受必须是互斥操作}// sudog 代表goroutinetype waitq struct {first *sudoglast *sudog}

3.Channel 操作符和操作方式
通信操作符
<-的箭头指示数据流向,箭头指向哪里,数据就流向哪里,它是一个二元操作符,可以支持任意类型,对于 channel 的操作只有4种方式:
- 创建 channel (通过make()函数实现,包括无缓存 channel 和有缓存 channel);
- 向 channel 中添加数据(channel<-data);
- 从 channel 中读取数据(data<-channel);
- data<-channel, 从 channel 中接收数据并赋值给 data
- <-channel,从 channel 中接收数据并丢弃
- 关闭 channel(通过 close()函数实现)
- 读取关闭后的无缓存通道,不管通道中是否有数据,返回值都为 0 和 false。
- 读取关闭后的有缓存通道,将缓存数据读取完后,再读取返回值为 0 和 false。
- 对于一个关闭的 channel,如果继续向 channel 发送数据,会引起 panic
- channel 不能 close 两次,多次 close 会 panic
4.Channel 有无缓冲 & 同步、异步
channel 分为有缓冲 channel 和无缓冲 channel,两种 channel 的创建方法如下:
- var ch = make(chan int) //无缓冲 channel,等同于make(chan int ,0),是一个同步的 Channel
- 无缓冲 channel 在读和写的过程中是都会阻塞,由于阻塞的存在,所以使用 channel 时特别注意使用方法,防止死锁和协程泄漏的产生。
- 无缓冲 channel 的发送动作一直要到有一个接收者接收这个值才算完成,否则都是阻塞着的,也就是说,发送的数据需要被读取后,发送才会完成
- 一般要配合 select + timeout 处理,然后再在这里添加超时时间
- var ch = make(chan int,10) //有缓冲channel,缓冲大小是10,是一个异步的Channel
- 带缓存的 channel 实际上是一个阻塞队列。队列满时写协程会阻塞,队列空时读协程阻塞。
- 有缓冲的时候,写操作是写完之后直接返回的。相对于不带缓存 channel,带缓存 channel 不易造成死锁。
5.Channel 各种操作导致阻塞和协程泄漏的场景
写操作,什么时候会被阻塞?
- 向 nil 通道发送数据会被阻塞
- 向无缓冲 channel 写数据,如果读协程没有准备好,会阻塞
- 无缓冲 channel ,必须要有读有写,写了数据之后,必须要读出来,否则导致 channel 阻塞,从而使得协程阻塞而使得协程泄漏
- 一个无缓冲 channel,如果每次来一个请求就开一个 go 协程往里面写数据,但是一直没有被读取,那么就会导致这个 chan 一直阻塞,使得写这个 chan 的 go 协程一直无法释放从而协程泄漏。
- 向有缓冲 channel 写数据,如果缓冲已满,会阻塞
- 有缓冲的 channel,在缓冲 buffer 之内,不读取也不会导致阻塞,当然也就不会使得协程泄漏,但是如果写数据超过了 buffer 还没有读取,那么继续写的时候就会阻塞了。如果往有缓冲的 channel 写了数据但是一直没有读取就直接退出协程的话,一样会导致 channel 阻塞,从而使得协程阻塞并泄漏。
读操作,什么时候会被阻塞?
- 从 nil 通道接收数据会被阻塞
- 从无缓冲 channel 读数据,如果写协程没有准备好,会阻塞
- 从有缓冲 channel 读数据,如果缓冲为空,会阻塞
close 操作,什么时候会被阻塞?
- close channel 对 channel 阻塞是没有任何效果的,写了数据但是不读,直接 close,还是会阻塞的。
6.Channel 各种操作对应的状态
- 正常的 channel,可读、可写
- nil 的 channel,表示未初始化的状态,只进行了声明,或者手动赋值为 nil
- 已经 closed 的 channel,表示已经 close 关闭了,千万不要误认为关闭 channel 后,channel 的值是 nil

7.Channel 长度和容量
容量(capacity)代表 Channel 容纳的最多的元素的数量,代表Channel的缓存的大小。如果没有设置容量,或者容量设置为0, 说明 Channel 没有缓存,长度和容量的两个函数是 cap 和 len 。
示例如下:
c := make(chan int, 100) // cap 就是 100,但是此时 len 为 0
c <- 0 // len = 1, cap = 100
c <- 0 // len = 2, cap = 100
<- c // len = 1, cap = 100
8.Channel 的缺点
Channel 的缺点:
- Channel 可能会导致循环阻塞或者协程泄漏,这个是最最最要重点关注的
- Channel 中传递指针会导致数据竞态问题(data race/ race conditions)
- Channel 中传递的都是数据的拷贝,可能会影响性能,但是就目前我们的机器性能来看,这点数据拷贝所带来的 CPU 消耗,大多数的情况下可以忽略
9.Go Channel 实现协程同步
channel 实现并发同步的说明:channel 作为 Go 并发模型的核心思想:不要通过共享内存来通信,而应该通过通信来共享内存,那么在 Go 里面,当然也可以很方便通过 channel 来实现协程的并发和同步了,并且 channel 本身还可以支持有缓冲和无缓冲的,通过 channel + timeout 实现并发协程之间的同步也是常见的一种使用姿势。
无缓冲 chan 示例
示例如下:
package main
import "fmt"
func main() {var ch = make(chan string)for i := 0; i < 10; i++ {go sum(i, i+10, ch)}for i := 0; i < 10; i++ {fmt.Print(<-ch)}
}
func sum(start, end int, ch chan string) {var sum int = 0for i := start; i < end; i++ {sum += i}ch <- fmt.Sprintf("Sum from %d to %d is %d\n", start, end, sum)
}
有缓冲 chan 示例
message_chan := make(chan int, 2)go func() {time.Sleep(time.Second * 3)println("start recv...")println(<-message_chan)println(<-message_chan)println(<-message_chan)println("finish recv...")}()println("start send 10...")message_chan <- 10println("start send 20...")message_chan <- 20println("start send 30...")message_chan <- 30println("finish send...")time.Sleep(time.Second * 3)close(message_chan)
参考
[go学习笔记.第十四章.协程和管道] 2.管道
Golang Channel 详细原理和使用技巧 - 知乎
Channels in Go(https://go101.org/article/channel.html)
How to Gracefully Close Channels(https://go101.org/article/channel-closing.html)
相关文章:
Golang Channel 详细原理和使用技巧
1.简介 Channel(一般简写为 chan) 管道提供了一种机制:它在两个并发执行的协程之间进行同步,并通过传递与该管道元素类型相符的值来进行通信,它是Golang在语言层面提供的goroutine间的通信方式.通过Channel在不同的 goroutine中交换数据,在goroutine之间…...
CSS的浮动属性,web前端开发工程师
了解校招 知己知彼才能百战百胜,在准备校招之前,我们先要了解校招。 什么是校招? 校招,全称校园招聘,指企业招聘那些即将毕业的学生。校招主要分为三个部分:简历筛选,笔试,面试。 …...
Dubbo的集群容错方案
Dubbo提供了多种集群容错方案来保证分布式环境下的高可用性。这些容错方案可以在服务提供者不可用时,根据不同的业务需求和场景,选择不同的策略来处理。以下是Dubbo支持的一些主要集群容错方案: 1. Failover Cluster(失败自动切换…...
两天学会微服务网关Gateway-Gateway路由规则
锋哥原创的微服务网关Gateway视频教程: Gateway微服务网关视频教程(无废话版)_哔哩哔哩_bilibiliGateway微服务网关视频教程(无废话版)共计17条视频,包括:1_Gateway简介、2_Gateway工作原理、3…...
three.js如何实现简易3D机房?(一)基础准备-下
three.js如何实现简易3D机房?(一)基础准备-上:http://t.csdnimg.cn/MCrFZ 目录 四、按需引入 五、导入模型 四、按需引入 index.vue文件中 <template><div class"three-area"><div class"three-box" ref"threeDemoRef…...
Android高级工程师面试实战,三幅图给你弄懂EventBus核心原理
阿里技术一面-35min 自我介绍 Android 有没有遇到OOM问题(有遇到内存泄漏问题)Handler机制ThreadLocalActivity启动到加载View过程View绘制过程LinearLayout (wrap_content) & TextView (match_parent) 最终结果???OKHttp(1. 为什么选择它? 2. 性能了解不…...
消息队列-kafka-服务端处理架构(架构,Topic文件结构,服务端数据的一致性)
服务端处理架构 资料来源于网络 网络线程池: 接受请求,num.network.threads,默认为 3,专门处理客户的发送的请求。 IO 线程池: num.io.threads,默认为 8,专门处理业务请求。也就是它不负责发…...
ES之API系列--index template(索引模板)的用法(有实例)
原文网址:ES之API系列--index template(索引模板)的用法(有实例)_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍ElasticSearch的index template(索引模板)的用法(有实例)。 官网网址 https://www.elastic.co/guide/en/elasticsearch/reference/8.0/index-temp…...
electron+vue3全家桶+vite项目搭建【28】封装窗口工具类【2】窗口组,维护窗口关系
文章目录 引入实现效果思路主进程模块渲染进程模块测试效果 引入 demo项目地址 窗口工具类系列文章: 封装窗口工具类【1】雏形 封装窗口工具类【2】窗口组,维护窗口关系 封装窗口工具类【3】控制窗口定向移动 我们思考一下窗口间的关系,窗…...
docker安装ES和kibana
文章目录 一、安装Elasticsearch1. 安装Elasticsearch2. 安装IK分词器3. elasticsearch-head 监控的插件4. 配置跨域 二、安装kibana 提示:以下是本篇文章正文内容,下面案例可供参考 一、安装Elasticsearch 1. 安装Elasticsearch 安装Elasticsearch参…...
uniapp微信小程序获取当前位置
uni-app微信小程序uni.getLocation获取位置;authorize scope.userLocation需要在app.json中声明permission;小程序用户拒绝授权后重新授权-CSDN博客...
HarmonyOS创建项目和应用—设置数据处理位置
项目和应用介绍 关于项目 项目是资源、应用的组织实体。资源包括服务器、数据库、存储,以及您的应用、终端用户的数据等。在您使用部分服务时,您是数据的控制者,数据将按照您设置的数据处理位置来存储在指定区域。 通常,您不需…...
3.1_2024ctf青少年比赛部分web题
php后门 根据x-powered-by知道php的版本 该版本存在漏洞: PHP 8.1.0-dev 开发版本后门 根据报错信息,进行提示,前 GET / HTTP/1.1 Host: challenge.qsnctf.com:31639 User-Agentt:12345678system(cat /flag);var_dump(2*3);zerodium12345678…...
Vue3:OptionsAPI 与 CompositionAPI的比较
1、Vue2 Vue2的API设计是Options(配置)风格的。 Options API 的弊端 Options类型的 API,数据、方法、计算属性等,是分散在:data、methods、computed中的,若想新增或者修改一个需求,就需要分别…...
Rust! 无VDom! 尤雨溪解析 Vue.js 2024 新特性
视频号搜索“云前端”观看视频版 在 VueJS Amsterdam 2024 大会首日,Vue 创始人 Evan You 进行了开场主旨演讲。他首先回顾了 Vue 十年以来的累累硕果,指出 VueJS 从一个视图层工具,成功演化出全流程的社区生态。 Vue 3.4 谈到 Vue 3 的发展时…...
Windows上websocket客户端连接定时存储消息到文件并加载文件定时发送服务端工具实现
场景 在业务开发中,需要对接三方websocket协议数据或者连接并存储线上websocket协议数据,需要使用websocket客户端 连接线上的websocket服务端获取并存储数据,然后将数据存储成文件格式可移植,并将数据复制 到本地,…...
【STM32+OPENMV】二维云台颜色识别及追踪
一、准备工作 有关OPENMV最大色块追踪及与STM32通信内容,详情见【STM32HAL】与OpenMV通信 有关七针OLED屏显示内容,详情见【STM32HAL】七针OLED(SSD1306)配置(SPI版) 二、所用工具 1、芯片:STM32F407ZGT6 2、CUBEMX配置软件 3、KEIL5 4…...
JavaScript基础3之面向对象关于面向过程、函数式编程、对比、构造函数、原型
JavaScript基础 面向对象面向过程函数式编程命令式编程函数式编程特性副作用透明引用不可变变量函数是一等公民 常见的函数式编程模型 面向对象为什么要使用面向对象封装继承多态 对比面向过程函数式编程面向对象 构造函数原型constructor使用场景 对象原型 面向对象 面向过程…...
运用Tensorflow进行目标检测
对象检测是一种计算机视觉技术,它使软件系统能够从给定的图像或视频中检测、定位并跟踪物体。对象检测的一个特殊属性是它能识别对象的类别(如人、桌子、椅子等)并在给定图像中指出其具体位置坐标。这个位置通常通过在物体周围绘制一个边界框…...
【源码】imx6ull实现触摸屏单点实验-移植tslib和qt
一、本实验实验的器材: 1.正点原子imx6ull的阿尔法开发板v2.2 2.屏幕ALIENTEK 4.3 RGBLCD 二、实验已经移植好的文件: 仓库代码:https://gitee.com/wangyoujie11/atkboard_-linux_-driver.git 1.文件说明 arm-qt.tar.bz2:移植好的…...
MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
LLMs 系列实操科普(1)
写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...
STM32HAL库USART源代码解析及应用
STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...
