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

【Go 基础】channel

Go 基础

channel

什么是channel,为什么它可以做到线程安全

Go 的设计思想就是:不要通过共享内存来通信,而是通过通信来共享内存。 前者就是传统的加锁,后者就是 channel。也即,channel 的主要目的就是在多任务间传递数据的,本身就是安全的。

  1. channel 是 Go 中的一个核心类型,它可以看作是一个管道,通过它并发核心单元就可以发送或者接收数据进行通讯。
  2. channel 也可以理解为一个先进先出队列,通过管道进行通信;
  3. 发送一个数据到 channel 和从 channel 中接收一个数据都是原子性的;

channel的生命周期状态有哪些

channel 存在 3 种状态:

  • nil:未初始化的状态,只进行了声明,或者手动赋值为 nil
  • active:正常的 channel,可以进行读写;
  • closed:已关闭,注意已关闭的 channel,它的值也不是 nil

针对不同状态的 channel,进行关闭,发送数据以及接收数据,会有不同的情况:

操作一个零值 nil 通道一个非零值但已关闭通道一个非零值且未关闭通道
关闭产生恐慌产生恐慌成功关闭
发送数据永久阻塞产生恐慌阻塞或者成功发送
接收数据永久阻塞永不阻塞(会立即返回零值阻塞或者成功接收

channel 的类型

channel 通常有以下三种类型:

  • 同步 channel:不需要缓冲区,发送方会直接将数据交给接收方;
  • 异步 channel:基于环形缓存的传统生产者消费者模型;
  • chan struct{}:这是专门用于协程间通信的标准信号,因为 struct{} 不占用内存空间,所以用的比较多;

Goroutine 和 channel 的作用分别是什么

这里可以先简单说下,进程、线程以及协程之间的关系。

进程是内存资源和 CPU 调度的执行单元。为了有效利用多核处理器的优势,将进程进一步细分,允许一个进程中存在多个线程,这多个线程还是共享同一片内存空间,但 CPU 调度的最小单元变成了线程。

而协程,可以看作是轻量级线程。但是,和线程不同的是,线程的切换是由操作系统控制的,而协程的切换是由用户控制的。

Go 中的 Goroutine 就是协程,可以实现并行,多个协程可以在多个处理器同时跑。而协程同一时刻只能在一个处理器上跑。多个 Goroutine 之间的通信就是通过 channel,而协程的通信是通过 yield 和 resume() 操作。

在 Golang 中 channel 是 goroutinues 之间进⾏通信的渠道。

可以把 channel 形象⽐喻为⼯⼚⾥的传送带,⼀头的⽣产者 goroutine 往传输带放东⻄,另⼀头的消费者 goroutinue 则从输送带取东⻄。channel 实际上是⼀个有类型的消息队列,遵循先进先出的特点。

goroutine 的使用

只需要在函数的调用前面加 go 关键字,就可以启动协程了:

func main() {for i:=1;i<5;i++ {go func(i int) {fmt.Println(i)}(i)}// 停歇5s,保证打印全部结束time.Sleep(5*time.Second)
}

上面的代码中,启动了 5 个 goroutine,再加上 main 函数的主 goroutine,一共 6 个 goroutine。由于 goroutine 类似于“守护线程”,是异步执行的。如果主 goroutine 不等待,程序可能就不会有打印输出了。

channel 的使用
  1. channel 的操作符号

    ch <- data 表示 data 被发送给 channel ch ;

    data <- ch 表示从 channel ch 取⼀个值,然后赋给 data;

  2. 阻塞式 channel

    channel 默认是没有缓冲区的,也即,通信是阻塞的。send 操作必须等到有消费者 accept 才算完成。

    func main() {ch1 := make(chan int)go pump(ch1)       // pump hangsfmt.Println(<-ch1) // prints only 1
    }func pump(ch chan int) {for i := 1; ; i++ {ch <- i}
    }
    

    在函数 pump() ⾥的 channel 在接受到第⼀个元素后就被阻塞了,直到主 goroutinue 取⾛了数据。最终channel 阻塞在接受第⼆个元素,程序只打印 1。

    没有缓冲的 channel 只能容纳⼀个元素,⽽带有缓冲 channel 则可以⾮阻塞容纳 N 个元素。发送数据到缓冲 channel 不会被阻塞,除⾮channel已满;同样的,从缓冲 channel 取数据也不会被阻塞,除⾮ channel 空了。

Go 中 channel 的实现

前文其实就一直有提到了:channel 是 Go 中 goroutines 之间的信息传递媒介,通过共享通信,来实现共享内存。

请添加图片描述

goroutine 通过使⽤ channel 传递数据,⼀个会向 Channel 中发送数据,另⼀个会从 Channel 中接收数据,它们两者能够独⽴运⾏并不存在直接关联,但是能通过 Channel 间接完成通信。

channel 的收发操作均遵循来先进先出的设计:

  • 先从 channel 读取数据的 goroutine 会先接收到数据
  • 先往 channel 发送数据的 goroutine 会得到先发送数据的权利
channel 在 runtime 中的具体实现

在 runtime.hchan 中定义了 channel:

type hchan struct {qcount   uint           // 当前队列⾥还剩余元素个数dataqsiz uint           // 环形队列⻓度,即缓冲区的⼤⼩,即make(chan T,N)中的Nbuf      unsafe.Pointer // 环形队列指针elemsize uint16         // 每个元素的⼤⼩closed   uint32         // 标识当前通道是否处于关闭状态,创建通道后,该字段设置0,即打开通道;通道调⽤close将其设置为1,通道关闭elemtype *_type         // 元素类型,⽤于数据传递过程中的赋值sendx    uint           // 环形缓冲区的状态字段,它只是缓冲区的当前索引-⽀持数组,它可以从中发送数据recvx    uint           // 环形缓冲区的状态字段,它只是缓冲区当前索引-⽀持数 组,它可以从中接受数据recvq    waitq          // 等待读消息的goroutine队列sendq    waitq          // 等待写消息的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 // 互斥锁,为每个读写操作锁定通道,因为发送和接受必须是互斥操作
}
type waitq struct {first *sudoglast  *sudog
}

其中,hchan 结构体中有五个字段是构建底层的循环队列:

  • qcount:channel 中剩余元素的个数
  • dataqsiz:channel 中循环队列的长度
  • buf:channel 的缓冲区数据指针
  • sendx:channel 的发送操作处理到的位置
  • recvx:channel 的接收操作处理到的位置

elemsize 和 elemtype 分别表示当前 channel 能够收发的元素类型和大小。

sendq 和 recvq 存储了当前 channel 由于缓冲区不足而阻塞的 goroutine 列表,这些等待队列使用双向链表 runtime.waitq 表示,链表中所有的元素都是 runtime.sudog 结构。

waitq 表示一个在等待队列中的 goroutine,该结构体存储了阻塞的相关信息以及两个分别指向前后 runtime.sudog 的指针。

创建 channel

runtime.makechan 和 runtime.makechan64 会根据传入的参数类型和缓冲区大小创建一个新的 channel 结构,其中后者用于处理缓冲区大小大于 2 的 32 次方的情况。

我们以 makechan 函数为例:

func makechan(t *chantype, size int) *hchan {elem := t.elem// compiler checks this but be safe.if elem.size >= 1<<16 {throw("makechan: invalid channel element type")}if hchanSize%maxAlign != 0 || elem.align > maxAlign {throw("makechan: bad alignment")}mem, overflow := math.MulUintptr(elem.size, uintptr(size))if overflow || mem > maxAlloc-hchanSize || size < 0 {panic(plainError("makechan: size out of range"))}// Hchan does not contain pointers interesting for GC when elements stored in buf do not contain pointers.// buf points into the same allocation, elemtype is persistent.// SudoG's are referenced from their owning thread so they can't be collected.// TODO(dvyukov,rlh): Rethink when collector can move allocated objects.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)if debugChan {print("makechan: chan=", c, "; elemsize=", elem.size, "; dataqsiz=", size, "\n")}return c
}

channel 中根据收发元素的类型和缓冲区的大小初始化 runtime.hchan 结构题和缓冲区:

在这里插入图片描述

arena 区域就是我们所谓的堆区,Go 动态分配的内存都是在这个区域,它把内存分割成 8KB 大小的页,一些页组合起来称为 mspan。

bitmap 区域标识 arena 区域哪些地址保存了对象,并且用 4bit 标志位表示对象是否包含指针、GC 标记信息。bitmap 中一个 byte 大小的内存对应 arena 区域中 4 个指针大小(指针大小为 8B)的内存,所以 bitmap 区域的大小是 512GB/(4*8B)=16GB。

在这里插入图片描述

此外,我们还可以看到 bitmap 的高地址部分指向 arena 区域的低地址部分,这里 bitmap 的地址是由高地址向低地址增长的。

spans 区域存放 mspan(是一些 arena 分割的页组合起来的内存管理基本单元)的指针,每个指针对应一页,所以 spans 区域的大小就是 512GB/8KB*8B=512MB。

在这里插入图片描述

除以 8KB 是计算 arena 区域的页数,而最后乘以 8 是计算 spans 区域所有指针的大小。创建 mspan 的时候,按页填充对应的 spans 区域,在回收 object 时,根据地址很容易就能找到它所属的 mspan。

相关文章:

【Go 基础】channel

Go 基础 channel 什么是channel&#xff0c;为什么它可以做到线程安全 Go 的设计思想就是&#xff1a;不要通过共享内存来通信&#xff0c;而是通过通信来共享内存。 前者就是传统的加锁&#xff0c;后者就是 channel。也即&#xff0c;channel 的主要目的就是在多任务间传递…...

windows10更新后system磁盘占用100%

windows10更新后system磁盘占用100% 现象&#xff1a; 解决办法&#xff1a; 打开服务禁用&#xff1a;Connected User Experiences and Telemetry 我现在已经把该服务禁用了&#xff0c;已经没有再出现不停写入的情况。 服务描述&#xff1a;“已连接的用户体验和遥测服务所…...

无人设备遥控器之防水性能篇

无人设备遥控器的防水性能是评估其耐用性和适应不同环境能力的重要指标。随着无人设备技术的不断发展&#xff0c;越来越多的遥控器在设计时融入了防水元素&#xff0c;以满足用户在不同天气条件下的使用需求。 一、防水等级与标准 无人设备遥控器的防水性能通常通过防水等级来…...

基于Matlab BP神经网络的非线性系统辨识与控制研究

随着现代工业和科学技术的不断发展&#xff0c;非线性系统的建模和控制成为了自动化领域中的重要研究课题。传统的系统辨识方法往往难以应对系统的复杂性和非线性特性&#xff0c;而人工神经网络&#xff08;ANN&#xff09;凭借其强大的逼近能力和自适应性&#xff0c;已广泛应…...

3D基因组工具(HiC可视化)trackc--bioinfomatics tools 35

01 3D genome data analysis guides 茶树三维基因组-文献精读19 https://trackc.readthedocs.io/en/latest/install.html #官网 https://github.com/seqyuan/trackc #官网https://trackc.readthedocs.io/en/latest/analysis_guide/index.html #HiC可视化案例 …...

【大模型微调】图片转pdf

有时候图片需要转成pdf https://www.bilibili.com/opus/982151156821131282 https://help.pdf24.org/ https://www.bilibili.com/video/BV163v2eyEWo/?vd_source=8318f88fcdf4948d2b21fae7c9cf3184 2024最新!小白如何安装破解版的 Acrobat https://www.32r.com/zt/dgyjzzrj/ …...

Linux-Ubuntu16.04摄像头 客户端抓取帧并保存为PNG

1.0&#xff1a;client.c抓取帧并保存为PNG #include <stdio.h> // 标准输入输出库 #include <stdlib.h> // 标准库&#xff0c;包含内存分配等函数 #include <string.h> // 字符串操作库 #include <linux/videodev2.h> // V4L2 视频设备…...

手机ip地址取决于什么?可以随便改吗

手机IP地址是指手机在连接到互联网时所获得的唯一网络地址&#xff0c;这个地址由一串数字组成&#xff0c;用于在网络中标识和定位设备。每个设备在连接到网络时都会被分配一个IP地址&#xff0c;它可以帮助数据包在网络中准确地找到目标设备。那么&#xff0c;手机IP地址究竟…...

计算机网络:TCP/IP协议的五大重要特性介绍

目录 一、逻辑编址 二、路由选择 三、名称解析 四、错误控制和流量控制 五、多应用支持 今天给大家聊聊TCP/IP协议中五大重要特性相关的知识,希望对大家深入了解该协议提供一些帮助! 一、逻辑编址 首先要了解什么是物理地址、逻辑地址。 ●...

Java与AWS S3的文件操作

从零开始&#xff1a;Java与AWS S3的文件操作 一、什么是 AWS S3&#xff1f;AWS S3 的特点AWS S3 的应用场景 二、Java整合S3方法使用 MinIO 客户端操作 S3使用 AWS SDK 操作 S3 &#xff08;推荐使用&#xff09; 三、总结 一、什么是 AWS S3&#xff1f; Amazon Simple Sto…...

详解 YOLOv5 模型运行参数含义以及设置及在 PyCharm 中的配置方法

详解 YOLOv5 模型运行参数含义以及设置及在 PyCharm 中的配置方法 这段代码中使用的命令行参数允许用户在运行 YOLOv5 模型时自定义多种行为和设置。以下是各个参数的详细说明和使用示例&#xff0c;以及如何在 PyCharm 中设置这些参数以确保正确运行带有参数的脚本。 命令行…...

Vue根据Div内容的高度给其Div设置style height

在 Vue.js 中&#xff0c;你可以使用 JavaScript 来动态地根据 div 的内容高度来设置其 style 的 height 属性。这通常是在组件挂载或更新时完成的&#xff0c;因为这时你已经有了实际的 DOM 元素可以操作。 以下是一个简单的例子&#xff0c;展示了如何实现这一点&#xff1a…...

驱动篇的开端

准备 在做之后的动作前&#xff0c;因为win7及其以上的版本默认是不支持DbgPrint&#xff08;大家暂时理解为内核版的printf&#xff09;的打印&#xff0c;所以&#xff0c;为了方便我们的调试&#xff0c;我们先要修改一下注册表 创建一个reg文件然后运行 Windows Registr…...

OpenSSL 自建CA 以及颁发证书(网站部署https双向认证)

前言 1、前面写过一篇 阿里云免费ssl证书申请与部署&#xff0c;大家可以去看下 一、openssl 安装说明 1、这部分就不再说了&#xff0c;我使用centos7.9&#xff0c;是自带 openssl的&#xff0c;window的话&#xff0c;要去下载安装 二、CA机构 CA机构&#xff0c;全称为…...

吾杯网络安全技能大赛WP(部分)

吾杯网络安全技能大赛WP(部分) MISC Sign 直接16进制解码即可 原神启动 将图片用StegSolve打开 找到了压缩包密码 将解出docx文件改为zip 找到了一张图片和zip 再把图片放到stegSlove里找到了img压缩包的密码 然后在document.xml里找到了text.zip压缩包密码 然后就出来fl…...

按vue组件实例类型实现非侵入式国际化多语言翻译

#vue3##国际化##本地化##international# web界面国际化&#xff0c;I18N&#xff08;Internationalization&#xff0c;国际化&#xff09;&#xff0c;I11L(International&#xff0c;英特纳雄耐尔)&#xff0c;L10N&#xff08;Localization&#xff0c;本地化&#xff09;&…...

Java入门:22.集合的特点,List,Set和Map集合的使用

1 什么是集合 本质就是容器的封装&#xff0c;可以存储多个元素 数组一旦创建&#xff0c;长度就不能再改变了。 数组一旦创建&#xff0c;存储内容的类型不能改变。 数组可以存储基本类型&#xff0c;也可以存储引用类型。 数组可以通过length获得容量的大小&#xff0c;但…...

重生之我在异世界学编程之C语言:深入指针篇(下)

大家好&#xff0c;这里是小编的博客频道 小编的博客&#xff1a;就爱学编程 很高兴在CSDN这个大家庭与大家相识&#xff0c;希望能在这里与大家共同进步&#xff0c;共同收获更好的自己&#xff01;&#xff01;&#xff01; 目录 题集&#xff08;1&#xff09;指针笔试题1&a…...

理解Parquet文件和Arrow格式:从Hugging Face数据集的角度出发

parquet发音&#xff1a;美 [pɑrˈkeɪ] 镶木地板&#xff1b;拼花木地板 理解Parquet文件和Arrow格式&#xff1a;从Hugging Face数据集的角度出发 引言 在机器学习和大数据处理中&#xff0c;数据的存储和传输格式对于性能至关重要。两种广泛使用的格式是 Parquet 和 Arr…...

下载 M3U8 格式的视频

要下载 M3U8 格式的视频&#xff08;通常是 HLS 视频流&#xff09;&#xff0c;可以尝试以下几种方法&#xff1a; 方法 1&#xff1a;使用下载工具&#xff08;推荐&#xff09; 1. IDM&#xff08;Internet Download Manager&#xff09;&#xff1a; 安装 IDM 并启用浏…...

Attention Is All You Need作者再出手:Transformer 99%稀疏,还能更快?

本文约2000字&#xff0c;建议阅读5分钟稀释不止省 FLOPs2017 年&#xff0c;《Attention Is All You Need》将 Transformer 推上深度学习主舞台。如今&#xff0c;几乎所有主流大模型都站在这套架构之上&#xff0c;推理、训练、显存和能耗成本也随模型规模一路上涨。大模型运…...

全同态加密与图机器学习在隐私保护反洗钱中的工程实践

1. 项目概述&#xff1a;当图机器学习遇上全同态加密在金融犯罪&#xff0c;尤其是反洗钱&#xff08;AML&#xff09;的战场上&#xff0c;我们一直面临一个核心矛盾&#xff1a;数据孤岛阻碍了协同作战的效能&#xff0c;而严格的隐私法规&#xff08;如GDPR&#xff09;又像…...

用if…elseif…end语句输出成绩等级

‌Matlab里面的if分支结构语句主要有单分支、双分支和多分支结构语句三种形式&#xff0c;前面两篇博文分别学习了单分支结构语句和双分支结构语句&#xff0c;这篇博文列出三种分支结构语句的特点&#xff0c;并对多分支结构语句进行学习。1、if…end语句if…end语句&#xff…...

可微分编程与强化学习在粒子探测器优化中的应用

1. 可微分编程在粒子探测器优化中的革新应用可微分编程&#xff08;Differentiable Programming&#xff09;正在彻底改变粒子探测器设计的传统范式。这种技术允许我们将整个探测器系统——从传感器几何形状到重建算法——构建为一个可微分的计算图。想象一下&#xff0c;这就像…...

避坑指南:CWGCNA因果分析前的数据准备与混杂因素处理(以DNA甲基化数据为例)

CWGCNA因果分析实战&#xff1a;从数据清洗到混杂因素校正的完整指南在生物信息学领域&#xff0c;DNA甲基化数据的因果分析正成为理解表观遗传调控机制的重要工具。CWGCNA&#xff08;因果加权基因共表达网络分析&#xff09;作为WGCNA的扩展方法&#xff0c;通过引入中介分析…...

[智能体-42]:深度解读:Python 免编译 + 动态执行,支撑智能体落地大模型决策

一、先厘清核心概念无需编译执行&#xff1a;Python 属于解释型语言&#xff0c;区别于 C/C、Java 编译型语言。编译型语言必须先将源码整体编译成机器码 / 字节码文件&#xff0c;才能运行&#xff1b;Python 无需手动编译&#xff0c;源码可逐行边解析边执行&#xff0c;即时…...

跨环境漏洞复现:Docker Desktop与VMware Kali的TCP/信号对齐实战

1. 这不是“复现个POC就完事”的演练&#xff0c;而是真实攻防链路上的环境卡点攻坚你有没有遇到过这种情况&#xff1a;在本地Kali虚拟机里跑通的CVE-2026-24061利用脚本&#xff0c;一放到客户现场的Docker Desktop环境里就报错——不是缺Python模块&#xff0c;就是socket连…...

FPGA加速机器学习在粒子物理触发系统中的应用与实战

1. 项目概述&#xff1a;当FPGA遇上机器学习&#xff0c;为粒子物理装上“火眼金睛” 在大型强子对撞机&#xff08;LHC&#xff09;的心脏地带&#xff0c;每秒发生着数亿次质子对撞。每一次对撞都可能产生希格斯玻色子、顶夸克&#xff0c;或是我们尚未知晓的新物理现象。然而…...

Evident方法论:用观察、假设、测试构建可复现的数据科学工作流

1. 项目概述&#xff1a;为什么我们需要一种新的数据科学方法论&#xff1f;干了十多年数据科学和机器学习项目&#xff0c;从初创公司到大型企业都待过&#xff0c;我越来越觉得&#xff0c;我们这行当的“工作方式”有点不对劲。项目周期总是难以预估&#xff0c;代码和数据像…...

从/dev/snd文件看起:手把手教你理解Linux ALSA声卡驱动的设备命名规则

从/dev/snd文件看起&#xff1a;手把手教你理解Linux ALSA声卡驱动的设备命名规则当你第一次打开/dev/snd目录&#xff0c;看到诸如controlC0、pcmC0D0p这样神秘的文件名时&#xff0c;是否感到困惑&#xff1f;这些看似随意的字符串背后&#xff0c;其实隐藏着ALSA驱动对音频硬…...