Channel最佳实践之基本规则【译】

Go-Channel的使用和底层原理(上) channel是Go语言的核心类型,可以理解为管道,通过channel并发核心单元就可以发送或者接收数据进行通讯 阅读详情

0?wx_fmt=jpeg

channel[通道]是golang的一种重要特性,正是因为channel的存在才使得golang不同于其它语言。channel使得并发编程变得简单容易有趣。

channel的概念和语法

一个channel可以理解为一个先进先出的消息队列。channel用来在协程[goroutine]之间传递数据,准确的说,是用来传递数据的所有权。一个设计良好的程序应该确保同一时刻channel里面的数据只会被同一个协程拥有,这样就可以避免并发带来的数据不安全问题[data races]。

channel的类型

像数组、切片和字典一样,channel类型是一种组合类型,每一种channel类型都对应着一种简单的数据类型。比如元素的类型是string,那么对应的channel类型就是chan string,进入channel的数据也就必须是string类型的值。

官方的go编译器限制channel里的单个元素最多65535个字节,也就是说如果channel缓冲数组里面容纳的是struct,那这个struct的size不能大过65535。尽管如此,我们也不应该传递体积过大的元素值,因为channel的数据从进入到流出会涉及到数据拷贝操作。如果元素体积过大,最好的方法还是使用传递指针来取代传递值。

channel类型是可以带有方向的,假设T是一种类型

  1. chan T是双向channel类型,编译器允许对双向channel同时进行发送和接收。

  2. chan<- T是只写channel类型,编译器只允许往channel里面发送数据。

  3. <-chan T是只读channel类型,编辑器只允许从channel里面接收数据。

双向类型的channel,可以被强制转换成只读channel或者是只写channel,但是反过来却不行,只读和只写channel是不可以转换成双向channel的。

channel类型的零值形式称为空channel。一个非空channel类型必须通过make关键字进行创建。例如make(chan int, 10)将会创建出一个可以容纳10个int值的channel。第二个整形的参数值代表的就是channel可以容纳数据的大小,如果不提供这个参数值,那默认值就是零。

var ch chan string; // nil channel
ch := make(chan string); // zero channel
ch := make(chan string, 10); // buffered channel

channel里面的value buffer的容量也就是channel的容量。channel的容量为零表示这是一个阻塞型通道,非零表示缓冲型通道[非阻塞型通道]。

channel内部结构

每个channel内部实现都有三个队列

  1. 接收消息的协程队列。这个队列的结构是一个限定最大长度的链表,所有阻塞在channel的接收操作的协程都会被放在这个队列里。

  2. 发送消息的协程队列。这个队列的结构也是一个限定最大长度的链表。所有阻塞在channel的发送操作的协程也都会被放在这个队列里。

  3. 环形数据缓冲队列。这个环形数组的大小就是channel的容量。如果数组装满了,就表示channel满了,如果数组里一个值也没有,就表示channel是空的。对于一个阻塞型channel来说,它总是同时处于即满又空的状态。

一个channel被所有使用它的协程所引用,也就是说,只要这两个装了协程的队列长度大于零,那么这个channel就永远不会被垃圾回收。另外,协程本身如果阻塞在channel的读写操作上,这个协程也永远不会被垃圾回收,即使这个channel只会被这一个协程所引用。

channel的使用

channel支持以下操作

  1. 使用cap(ch)函数查询channel的容量,cap是golang的内置函数

  2. 使用len(ch)函数查询channel内部的数据长度,len函数也是内置的,表面上这个函数很有意义,但实际上它很少用。

  3. 使用close(ch)关闭channel,close也是内置函数。一个非空channel只能够被关闭一次,如果关闭一个已经被关闭的或者是关闭一个空channel将会引发panic。另外关闭一个只读channel是非法的,编译器直接报错。

  4. 使用ch <- v发送一个值v到channel。发送值到channel可能会有多种结果,即可能成功,也可能阻塞,甚至还会引发panic,取决于当前channel在什么状态。

  5. 使用 v, ok <- ch 接收一个值。第二个遍历ok是可选的,它表示channel是否已关闭。接收值只会又两种结果,要么成功要么阻塞,而永远也不会引发panic。

所有的这些操作都是同步的协程安全的,不需要加任何其它同步控制。

For-Range

for-range语法可以用到通道上。循环会一直接收channel里面的数据,直到channel关闭。不同于array/slice/map上的for-range,channel的for-range只允许有一个变量。

for v = range aChannel {
	// use v
}

等价于

for {
	v, ok = <-aChannel
	if !ok {
		break
	}
	// use v
}

注意,for-range对应的channel不能是只写channel。

Select-Cases

select块是为channel特殊设计的语法,它和switch语法非常相近。分支上它们都可以有多个case块和做多一个default块,但是也有很多不同

  1. select 到 括号{之间不得有任何表达式

  2. fallthrough关键字不能用在select里面

  3. 所有的case语句要么是channel的发送操作,要么就是channel的接收操作

  4. select里面的case语句是随机执行的,而不能是顺序执行的。设想如果第一个case语句对应的channel是非阻塞的话,case语句的顺序执行会导致后续的case语句一直得不到执行除非第一个case语句对应的channel里面的值都耗尽了。

  5. 如果所有case语句关联的操作都是阻塞的,default分支就会被执行。如果没有default分支,当前goroutine就会阻塞,当前的goroutine会挂接到所有关联的channel内部的协程队列上。 所以说单个goroutine是可以同时挂接到多个channel上的,甚至可以同时挂接到同一个channel的发送协程队列和接收协程队列上。当一个阻塞的goroutine拿到了数据接触阻塞的时候,它会从所有相关的channel队列中移除掉。

channel简单规则表

下标的活跃Channel表示即非空又非关闭的Channel

0?wx_fmt=jpeg

channel规则详细解释

空channel

  1. 关闭一个空channel会导致当前goroutine引发panic

  2. 向一个空channel发送值会导致当前的goroutine阻塞

  3. 从一个空channel接收值也会导致当前的goroutine阻塞

在空channel上的调用len和cap函数都统一返回零。

已关闭的Channel

  1. 关闭一个已关闭的channel会引发panic

  2. 向一个已关闭的channel发送值会引发panic。当这种send操作处于select块里面的case语句上时,它会随时导致select语句引发panic。

  3. 从一个已关闭的channel上接收值既不会阻塞也不能panic,它一直能成功返回。只是返回的第二个值ok永远是false,表示接收到的v是在channel关闭之后拿到的,对应得值也是相应元素类型的零值。可以无限循环从已关闭的channel上接收值。

活跃的Channel

  1. 关闭操作

    1. 从channel的接收协程队列中移除所有的goroutine,并唤醒它们。

    2. 从channel的接收协程队列中移除所有的goroutine,并唤醒它们。

    3. 一个已关闭的channel内部的缓冲数组可能不是空的,没有接收的这些值会导致channel对象永远不会被垃圾回收。

  2. 发送操作

    1. 如果是阻塞型channel,那就从channel的接收协程队列中移出第一个协程,然后把发送的值直接递给这个协程。

    2. 如果是阻塞型channel,并且channel的接收协程队列是空的,那么当前的协程将会阻塞,并进入到channel的发送协程队列里。

    3. 如果是缓冲型channel,并且缓冲数组里还有空间,那么将发送的值添加到数组最后,当前协程不阻塞。

    4. 如果是缓冲型channel,并且缓冲数组已经满了,那么当前的协程将会阻塞,并进入到channel的发送协程队列中。

  3. 接收操作

    1. 如果是缓冲型channel,并且缓冲数组有值,那么当前的协程不会阻塞,直接从数组中拿出第一个值。如果发送队列非空,还需要将队列中的第一个goroutine唤醒。

    2. 如果是阻塞型channel,并且发送队列非空的话,那么唤醒发送队列第一个协程,该协程会将发送的值直接递给接收的协程。

    3. 如果是缓冲型channel,并且缓冲数组为空,或者是阻塞型channel,并且发送协程队列为空,那么当前协程将会阻塞,并加入到channel的接收协程队列中。

总结

根据以上规则,我们可以得出以下结论

  1. 如果channel关闭了,那么它的接收和发送协程队列必然空了,但是它的缓冲数组可能还没有空。

  2. channel的接收协程队列和缓冲数组,同一个时间必然有一个是空的

  3. channel的缓冲数组如果未满,那么它的发送协程队列必然是空的

  4. 对于缓冲型channel,同一时间它的接收和发送协程队列,必然有一个是空的

  5. 对于非缓冲型channel,一般来说同一时间它的接收和发送协程队列,也必然有一个是空的,但是有一个例外,那就是当它的发送操作和接收操作在同一个select块里出现的时候,两个队列都不是空的。

0?wx_fmt=jpeg

Golang Channel 最佳实践:写出更优雅的并发代码 本文旨在为Golang开发者提供关于Channel的全面指南,涵盖从基础概念到高级用法的各个方面。我们将重点讨论如何避免常见的并发陷阱,以及如何利用Channel的特性编写更健壮的并发代码。核心概念与联系:解释Channel的基本原理和goroutine的关系核心算法原理:深入分析Channel的内部工作机制项目实战:通过实际案例展示Channel的最佳实践实际应用场景:讨论Channel在不同场景下的应用工具和资源推荐:分享有用的调试和性能分析工具。 阅读详情

相关推荐

【Golang】Go高并发之线程间数据通信Channel原理解析与应用实战

在Go语言中,Channel是一种强大的并发通信工具,用于在Goroutine之间安全地传递数据。 通过Channel,我们可以实现并发通信和同步操作,确保数据的安全传输。 本文将详细介绍Go语言中的Channel,包括其创建、发送、接收、关闭等操作,以及一些常见的使用场景和高级特性。

景天科技苑 5万+

golang中channel使用

Go并发是源自CSP模型,通过channel来实现协程的同步,Go并发不通过共享内存来通信,而是通过通信来共享内存,Go内建channel实现了go协程之间数据的读写相关操作,通道(channel)是一种特殊的类型,在任何时候,同时只能有一个 goroutine 访问通道进行发送和获取数据。channel可以看作一个消息队列,遵循先进先出的原则,从而保证了收发数据的顺序性。channel本身是一个队列,先进先出线程安全,不需要枷锁,但是如果传递数组指针或者其他非线程安全的指针或引用,需要额外做好保护。...

技术控的笔记 8万+

STM32 RC522串口驱动程序 (简单直接)

rc522作为使用非常多的读写卡芯片,使用SPI芯片读写应用已经非常广了。但有些时候我们只有串口可以使用,或者我们觉得串口使用更加方便,那么就必须要编写串口的驱动程序。不过很难找到可以直接使用的串口程序。所以在此将我这边写好的串口驱动写好如下: 直接上代码 下面是RC522.C的驱动文件。支持读写卡号8位数据。rc522用的是stm32 的串口3,调试显示是串口1; #include "rc522.h" #include "delay.h" /////////////////////////////////

qq_42074368的博客 9001

GO:channel

文章目录概念使用demo向channel发送一个数据并接收向channel连续发送数据并接收创建channel说明接收channel说明 概念 channel是goroutine之间的通信机制。每个channel都有一个数据类型,指定发送该类型的数据;同时同一时刻只有一个goroutine可以访问channel进行数据发送或访问;channel遵循FIFO原则。 channel的数据传输有以下特点: channel的发送方和接收方是不同的goroutine(因为数据的发送和接收行为都会阻塞); chann

AXIMI的博客 394

从SD更新字库到FLASH,再用LCD显示

用Flash当字库存储载体。思路是先将要用到的字库文件存在SD卡,然后存储到Flash,LCD显示时候从Flash中读取 还挺顺利的,移植的原子的程序,自己稍加修改,主要是我的液晶屏函数,Flash读写函数,还有存储文件的flash地址不一样。一晚上搞定,大小字体都OK。挺高兴哈哈。这几天忙工作事情有点耽搁了学习进展嘿嘿 在text.c的showstr函数中char的字体颜色随意设置的,可以显...

weixin_30505751的博客 914

MATLAB信号处理实战:VMD分解参数调优全指南(附避坑技巧)

本文深入探讨了MATLAB中变分模态分解(VMD)的核心参数调优实战技巧。针对模态数K和惩罚系数α的选择难题,提供了从经验估计、可视化观察到量化指标辅助的渐进式调优框架,并分享了避免过分解与欠分解的实用避坑指南,旨在帮助工程师高效处理非平稳信号,提取清晰的物理特征。

weixin_29079671的博客 146

在一个nil的channel上发送和接收操作会被永久阻塞

/ 这里比hub 发送时候有延迟时间,这段延迟时间是否会对结果产生影响。这里一定要注意,以下问题导致gorounting阻塞。

silver9886的专栏 1091

Seedance 2.5、HappyHorse 1.1与豆包Seed 2.1:一周内三大模型技术参数分析

音频生成延续原生音画同步架构,将文本、画面、音频统一序列建模,生成过程同步产出匹配口型的原声音频,支持中、英、日、韩、德、法、阿拉伯7种语言唇形对齐,无需二次调音。从行业趋势看,30秒直出、4K分辨率、7语种唇形同步正在成为视频生成的标配,编程Agent化和多模态理解正在成为语言模型的标配。技术狂奔的同时,监管在同步收紧。我理解这里的关键在于,视频生成模型的商业化落地不仅取决于生成质量,更取决于推理效率——生成一段15秒1080P视频需要多少秒、消耗多少算力,直接决定了模型的商用可行性。

lichunchuan1的博客 328

golang 中 channel 的详细使用、使用注意事项及死锁分析

什么是 channel 管道 它是一个数据管道,可以往里面写数据,从里面读数据。 channel 是 goroutine 之间数据通信桥梁,而且是线程安全的。 channel 遵循先进先出原则。 写入,读出数据都会加锁。 channel 可以分为 3 种类型: 只读 channel,单向 channel 只写 channel,单向 channel 可读可写 channel channel 还可按是否带有缓冲区分为: 带缓冲区的 channel,定义了缓冲区大小,可以存储多个数据 不带

九卷技术录 8777

富芮坤FR801xH蓝牙协议栈启动流程和notify实现温度数据主动上传

文章目录一、蓝牙协议栈启动流程1.1 初始化代码1.2 初始化流程1.3 回调函数里的初始化二、notify实现2.1 notify介绍2.2 notify实现2.3.1 定义数据2.3.2 获取连接号conidx2.3.3 gatt_notification函数的使用三、notify温度上传测试3.1 蓝牙调试工具3.2 蓝牙调试器设置3.3 测试结果四、参考链接   基于sdk开发的好处是方便项目的快速落地,现在用的是SDK里面的源文件,目前只是学习FR801xH芯片的特性,裁剪这个SDK需要点时间,

weixin_43810563的博客 4320

Ascend C 算子开发遇到的问题及解决方法

在学习Ascend C算子开发进阶课程的时候,进行Ascend C自定义算子工程、算子调用等实验,在开发环境中遇到了一些问题,在这里记录一下。首先如果在启智社区CANN版本为6.3 ,要进行Ascend C算子开发,需要更新CANN版本。

qq_45257495的博客 5109

channel基本介绍狗朗

channel[通道]是golang的一种重要特性,正是因为channel的存在才使得golang不同于其它语言。channel使得并发编程变得简单容易有趣。 channel的概念和语法 一个channel可以理解为一个先进先出的消息队列。channel用来在协程[goroutine]之前传递数据,准确的说,是用来传递数据的所有权。一个设计良好的程序应该确保同一时刻channel里面的数据只会...

够浪 1万+

Swin Transformer 与 CNN 结合实现图像分类

Swin Transformer 与 CNN 结合实现图像分类。(含swin transformer 结构讲解,cnn网络搭建,代码)

这里是Mae。 5027

go语言channel学习与总结(一)channel的一些基本用法

go语言中,说到并发先想到到就是goroutine,在go程序中,我们可以执行成千上万个goroutine协程,但是只是单纯到并发执行某个函数意义是不大的,既能够在函数与函数之间交换数据,又能使成千上万的函数一起执行,这才是go语言设计的精妙绝伦。 Go语言的并发模型是CSP(Communicating Sequential Processes),提倡通过通信共享内存而不是通过共享内存而实现通信。 这句话就是channel的核心设计理念。 channel在Go 语言中是一种特殊的类型(引用类型)。ch

shalaoq的博客 10万+

Python全栈入门到实战【基础篇 19】函数进阶:默认参数、递归函数与偏函数应用

本文深入探讨Python函数进阶的三大核心知识:默认参数、递归函数与偏函数。针对默认参数,揭示了可变对象作为默认值的陷阱,提出使用None替代的方案;解析递归函数的三大要素(终止条件、递归调用、问题拆解),并通过阶乘和斐波那契数列示例演示实现方法,同时给出尾递归和缓存装饰器两种优化策略;最后介绍偏函数如何通过functools.partial固化参数简化调用。这些技巧能显著提升函数在复杂场景下的灵活性与复用性,帮助开发者编写更高效的Python代码。

那些看似不起波澜的日复一日,会在某天让你看到坚持的意义 3864

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

channel 的 15 条规则和底层实现

GopherChina2023大会倒计时3天!本次大会邀请到了来自多家顶尖技术平台、企业和开发行业的资深专家及研发工程师们。针对当下最前沿的技术发展趋势与应用案例,分享他们多年积累的 Go 语言操作实战经验、项目开发经验、Go 项目设计经验及相关理论知识等。大会干货满满,千万别错过!抓紧最后的时间报名啦!!(详细信息请参考文末的阅读原文)概述 下面表格中的内容是 Go 语言中 channel 数据...

Go中国 1582

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

channel基本介绍·GoLang

channel基本介绍·GoLang

了不起的自己 923

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Go channel——基本机制及结构

前言 GO官方博客的Share Memory By Communicating中提到: Go’s concurrency primitives - goroutines and channels - provide an elegant and distinct means of structuring concurrent software. (These concepts have an interesting history that begins with C. A. R. Hoare’s Com

yuchenfw的博客 636

Go Channel 深度解析:从底层实现到并发最佳实践(2026前瞻版)

Go语言并发编程的核心支柱Channel详解 摘要:Channel作为Go语言并发模型的核心机制,实现了"通过通信共享内存"的理念。本文深入解析Channel的本质特性、核心价值及高级应用。Channel本质是线程安全的FIFO队列,分为无缓冲(同步)和有缓冲(异步)两种类型,各自具有特定的阻塞规则。文章详细拆解了Channel的阻塞场景、关闭机制和安全操作规范,并介绍了单向Channel的权限控制特性。高级用法部分探讨了for-range遍历、select多路复用等实践技巧。通过系统掌

从零开始,掌握网络安全。提供全面的网络安全教程、攻防演练技巧和行业动态。 525

golang 关闭channel的正确姿势

channel是什么不多赘言。go关键字+channel,写并发那叫一个爽。 但经常有些“痛点”被提及: 关闭一个已经被关闭的channel会导致panic。 像一个已经关闭的channel发送数据会导致panic。(特别是多生产者的状况下) 这个时候心里就吐槽,为什么没有提供一个函数比如 IsClosed() 来判断一个channel是否被关闭了呢? 立刻设计一个: fu...

weixin_30251829的博客 2348

【Golang】--- Channel

Go语言Channel是实现goroutine并发通信的核心机制,贯彻“通过通信共享内存”的设计理念。本文介绍Channel基本概念与创建方式,分为无缓冲和有缓冲两类:无缓冲Channel实现同步通信,发送与接收需同时就绪;有缓冲Channel支持异步通信,依靠缓冲区暂存数据。文章详细讲解发送、接收、关闭三大核心操作,通过代码示例展示基本使用、带状态判断的接收及for-range简化读取,清晰对比两类通道的特性差异,帮助读者快速掌握Channel基础用法与并发通信逻辑。

2401_87022967的博客 1013

【golang】channel原理和机制

在select非阻塞模式下,读/写 channel 方法通过一个 bool 型的响应参数,用以标识是否读取/写入成功,实际并不阻塞,而是cas轮询。此时会单纯因为缓冲区满而阻塞该写协程,将其加入到写阻塞队列中,主动park阻塞。读阻塞意味着写入前缓冲区为空,此时写协程会直接从阻塞队列里取出一个协程,直接memmove数据到对方,并唤醒该协程。如果channel无缓冲区,直接唤醒该写协程,读取其元素,也就是强同步的逻辑。也就是目前无法读到数据的情况,将该协程加入读阻塞队列,主动park陷入阻塞。

欢迎指正、交流 4214

深度解密Go语言之channel

目录 并发模型 并发与并行 什么是 CSP 什么是 channel channel 实现 CSP 为什么要 channel channel 实现原理 数据结构 创建 ...

weixin_30257433的博客 2945

golang之channel学习总结

概述 本篇目的是对go中的channel做一个总结。 主要参考https://www.jianshu.com/p/76acce09da09 环境 $ uname -a Linux gl.com 5.4.50-amd64-desktop #74 SMP Mon Aug 24 20:15:37 CST 2020 x86_64 GNU/Linux $ go version go version go1.15.2 linux/amd64 channel的用途 主要用于goroutine之间通信 channel的

开心就好的专栏 1215

Golang—channel

所以,通俗地讲,Channel 的作用它为协程们提供了一条安全、有序的“数据传输管道”。不仅解决了“怎么传”的通信问题,更重要的是通过“等待”机制,巧妙地解决了“什么时候传”的同步问题,从而让并发编程变得简单和安全。make(chan Type) //无缓冲 等价于make(chan Type,0)make(chan Type, capacity) //有缓冲channel <- value //发送value到channel<- channel //接收并将其丢弃。

qq_48512649的博客 821
上一篇: 如何优雅的关闭Go Channel【译】
下一篇: 深度学习Java之内存模型【译】
码洞
博客等级 码龄10年 41粉丝 137原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值