1. 引言

"Do not communicate by sharing memory; instead, share memory by communicating." (不要通过共享内存来通信,而应通过通信来共享内存。) 这是 Go 语言并发设计的核心哲学。而 channel 正是实现这一哲学的核心工具。

Channel 为 Goroutine 之间的通信提供了安全的、同步的机制。它究竟是如何在底层保证并发安全和实现阻塞/非阻塞操作的?本文将深入其源码,揭示 channel 的内部奥秘。

2. Channel 的核心数据结构

在 Go 的 runtime/chan.go 源码中,channel 的底层实现是一个名为 hchan 的结构体。其核心字段如下(有简化):

// src/runtime/chan.go
type hchan struct {qcount   uint           // channel 中当前的元素个数dataqsiz uint           // channel 的容量(环形队列的大小)buf      unsafe.Pointer // 指向容量大小为 dataqsiz 的环形队列elemsize uint16         // channel 中元素的大小closed   uint32         // 标记 channel 是否关闭sendx uint // 环形队列的发送索引recvx uint // 环形队列的接收索引recvq waitq // 等待接收的 goroutine 队列 (sudog 链表)sendq waitq // 等待发送的 goroutine 队列 (sudog 链表)lock mutex // 保证 channel 操作的原子性
}type waitq struct {first *sudoglast  *sudog
}

核心组件解析

  • buf (环形队列): 对于带缓冲的 channel,buf 是一个环形队列,用于存储元素。发送和接收操作通过移动 sendxrecvx 索引来完成。

  • lock (互斥锁): channel 的所有操作(发送、接收、关闭)都必须先获取这个锁,这保证了其并发安全性。

  • sendqrecvq (等待队列): 这是 channel 实现阻塞和唤醒的关键。

    • 当一个 goroutine 尝试向一个已满的 channel 发送数据时,它会被打包成一个 sudog(goroutine 在运行时的表示)并加入到 sendq 等待队列中,然后该 goroutine 会被挂起(park)

    • 当一个 goroutine 尝试从一个空的 channel 接收数据时,它也会被加入到 recvq 等待队列中并被挂起。

3. Channel 的操作原理
3.1 发送操作 (ch <- data)
  1. 加锁lock.Lock()

  2. 检查 closed 标志:如果 channel 已关闭,直接 panic

  3. 检查 recvq:如果接收等待队列 recvq 不为空,说明有 goroutine 正在等待接收数据。

    • 这是无缓冲 channel空缓冲 channel的接收者。

    • 直接将要发送的数据拷贝给等待的 goroutine。

    • 唤醒(gounpark)该 goroutine。

    • 解锁,发送完成。

  4. 检查 buf:如果 buf (环形队列) 还有空间 (qcount < dataqsiz)。

    • 将数据拷贝到 bufsendx 位置。

    • sendx 索引递增。

    • qcount 递增。

    • 解锁,发送完成。

  5. 阻塞发送:如果 recvq 为空且 buf 已满。

    • 将当前 goroutine 和要发送的数据打包成 sudog

    • 加入 sendq 发送等待队列。

    • 挂起当前 goroutine (gopark),并解锁。goroutine 会在此等待,直到有接收者将其唤醒。

3.2 接收操作 (<-ch)
  1. 加锁lock.Lock()

  2. 检查 sendq:如果发送等待队列 sendq 不为空。

    • 这通常发生在无缓冲 channel满缓冲 channel

    • sendq 中取出一个等待的 goroutine。

    • 如果 buf 为空,直接从该 goroutine 中取出数据。

    • 如果 buf 已满,先将 buf 的队首元素取出作为返回值,然后将等待 goroutine 的数据存入 buf 队尾。

    • 唤醒该发送 goroutine。

    • 解锁,接收完成。

  3. 检查 buf:如果 buf 中有数据 (qcount > 0)。

    • bufrecvx 位置取出数据。

    • recvx 索引递增。

    • qcount 递减。

    • 解锁,接收完成。

  4. 检查 closed 标志:如果 channel 已关闭且 buf 为空,立即返回元素类型的零值。

  5. 阻塞接收:如果上述条件都不满足。

    • 将当前 goroutine 打包成 sudog

    • 加入 recvq 接收等待队列。

    • 挂起当前 goroutine (gopark) 并解锁

4. select 的实现

select 语句的实现更为复杂,它会将涉及到的所有 case 构建成一个 scase 数组,然后通过 selectgo 函数执行以下逻辑:

  1. 随机轮询:打乱 scase 数组的顺序,防止优先级问题。

  2. 非阻塞检查:遍历所有 case,检查是否有任何一个 channel 可以立即进行非阻塞的发送或接收。如果有,则执行该操作并返回。

  3. 阻塞等待:如果所有 case 都无法立即完成,将当前 goroutine 加入到所有相关 channel 的等待队列中,然后挂起。

  4. 唤醒:当任何一个 channel 的操作条件满足时(例如,有数据被发送进来),对应的 channel 会唤醒这个等待的 goroutine。goroutine 被唤醒后,会完成相应的 case 操作。

5. 总结

Go channel 的底层是一个由互斥锁、环形队列和两个等待队列(sudog 链表)组成的精密结构。正是通过 lock 保证了并发安全,通过 sendqrecvq 配合调度器的 goparkgounpark,实现了 goroutine 之间的同步与通信。理解这一机制,有助于我们更深刻地运用 Go 的并发能力。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/91701.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/91701.shtml
英文地址,请注明出处:http://en.pswp.cn/web/91701.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Golang 语言的编程技巧之类型

1、介绍Golang 语言是一门静态类型的编程语言&#xff0c;我们在编写代码时&#xff0c;为了提升代码的灵活性&#xff0c;有时会使用空接口类型&#xff0c;对于空接口类型的变量&#xff0c;一般会通过类型断言判断变量的类型&#xff0c;而且可能还会遇到遇到类型转换的场景…

计数组合学7.11(RSK算法)

7.11 RSK算法 在对称函数理论中&#xff0c;有一个非凡的组合对应关系&#xff0c;称为RSK算法。&#xff08;关于缩写RSK的含义以及其他名称&#xff0c;请参阅本章末尾的注释。&#xff09;这里我们仅介绍RSK算法的最基本性质&#xff0c;从而能够给出舒尔函数一些基本性质的…

国产嵌入式调试器之光? RT-Trace 初体验!

做过嵌入式开发的工程师肯定都知道有这么个玩意儿 —— J-Trace&#xff0c;与我们日常使用的普通调试器不同点在于&#xff0c;它在基本的下载/调试代码之上还具有非常强大的代码运行跟踪能力&#xff0c;从而实现代码覆盖率的分析、指令回溯、CPU 资源监控等一系列强大的功能…

SLAM中的非线性优化-2D图优化之零空间实战(十六)

终于有时间更新实战篇了&#xff0c;本节实战几乎包含了SLAM后端的所有技巧&#xff0c;其中包括&#xff1a;舒尔补/先验Factor/鲁棒核函数/FEJ/BA优化等滑动窗口法的相关技巧&#xff0c;其中构建2D轮式里程计预积分以及绝对位姿观测的10帧滑动窗口&#xff0c;并边缘化最老帧…

知识随记-----Qt 实战教程:使用 QNetworkAccessManager 发送 HTTP POST

文章目录Qt 网络编程&#xff1a;使用 QNetworkAccessManager 实现 HTTP POST 请求概要整体架构流程技术名词解释技术细节注意事项&#xff1a;Qt 网络编程&#xff1a;使用 QNetworkAccessManager 实现 HTTP POST 请求 概要 本文介绍如何使用 Qt 框架的网络模块&#xff08;…

wordpress批量新建产品分类

1、下载安装插件&#xff1a;bulk-category-import-export2、激活插件后&#xff0c;左侧点击插件下的导入&#xff0c;选择product categories&#xff0c;点击下一步3、这里可以选择导入的分类列表文件&#xff0c;可以选择分隔符&#xff0c;CSV文件默认为‘&#xff0c;’要…

CentOS 镜像源配置与 EOL 后的应对策略

引言 本文将详细介绍如何使用 阿里云开源镜像站 配置 CentOS 的各类软件源&#xff0c;包括基础源、历史归档源&#xff08;vault&#xff09;、ARM 架构源、Stream 版本以及调试信息源&#xff08;debuginfo&#xff09;&#xff0c;并重点讲解在 CentOS 8 停止维护后&#x…

CTF实战:用Sqlmap破解表单输入型SQL注入题(输入账号密码/usernamepassword)

目录 引言 步骤1&#xff1a;用Burp Suite捕获表单请求 步骤2&#xff1a;用Sqlmap获取数据库名称 参数解释&#xff1a; 输出示例&#xff08;根据题目环境调整&#xff09;&#xff1a; 步骤3&#xff1a;获取目标数据库中的表名 参数解释&#xff1a; 输出示例&#…

质数时间(二分查找)

题目描述如果把一年之中的某个时间写作 a 月 b 日 c 时 d 分 e 秒的形式&#xff0c;当这五个数都为质数时&#xff0c;我们把这样的时间叫做质数时间&#xff0c;现已知起始时刻是 2022 年的 a 月 b 日 c 时 d 分 e 秒&#xff0c;终止时刻是 2022 年的 u 月 v 日 w 时 x 分 y…

Python训练Day29

浙大疏锦行 类的装饰器装饰器思想的进一步理解&#xff1a;外部修改、动态类方法的定义&#xff1a;内部定义和外部定义

新手DBA实战指南:如何使用gh-ost实现MySQL无锁表结构变更

新手DBA实战指南:如何使用gh-ost实现MySQL无锁表结构变更 作为DBA,大表结构变更(DDL)一直是令人头疼的问题。传统的ALTER TABLE操作会锁表,严重影响业务连续性;而常见的pt-online-schema-change工具虽然能实现在线变更,但依赖触发器机制,在高并发场景下性能表现不佳。本…

OSPF综合

一、实验拓扑二、实验需求1、R4为ISP&#xff0c;其上只配置IP地址&#xff1b;R4与其他所直连设备间均使用公有IP&#xff1b; 2、R3-R5、R6、R7为MGRE环境&#xff0c;R3为中心站点&#xff1b; 3、整个OSPF环境IP基于172.16.0.0/16划分&#xff1b;除了R12有两个环回&#x…

技术面试知识点详解 - 从电路到编程的全栈面经

技术面试知识点详解 - 从电路到编程的全栈面经 目录 模拟电路基础数字电路原理电源设计相关编程语言基础数据库与并发网络协议基础算法与数据结构 模拟电路基础 1. 放大电路类型判断 这是模拟电路面试的经典题目&#xff0c;通过电压放大倍数判断放大电路类型&#xff1a; …

LangGraph认知篇-Command函数

Command简述 在 LangGraph 中&#xff0c;Command 是一个极具实用性的功能&#xff0c;它能够将控制流&#xff08;边&#xff09;和状态更新&#xff08;节点&#xff09;巧妙地结合起来。这意味着开发者可以在同一个节点中&#xff0c;既执行状态更新操作&#xff0c;又决定下…

【目标检测】小样本度量学习

小样本度量学习&#xff08;Few-Shot Metric Learning&#xff09;通常用于分类任务​&#xff08;如图像分类&#xff09;&#xff0c;但它也可以与目标检测&#xff08;Object Detection&#xff09;结合&#xff0c;解决小样本目标检测&#xff08;Few-Shot Object Detectio…

cmd怎么取消关机命令

在 Windows 的命令提示符&#xff08;CMD&#xff09;中取消已计划的关机操作&#xff0c;可以通过 shutdown 命令的 ​**-a**​ 参数实现。以下是具体步骤&#xff1a;​操作方法​​打开 CMD​按下 Win R 组合键&#xff0c;输入 cmd 并回车&#xff0c;打开命令提示符窗口。…

网易云音乐硬刚腾讯系!起诉SM娱乐滥用市场支配地位

企查查APP显示&#xff0c;近日&#xff0c;法院公开杭州乐读科技有限公司、杭州网易云音乐科技有限公司起诉SM ENTERTAINMENT CO. 、卡斯梦&#xff08;上海&#xff09;文化传播有限公司等开庭信息&#xff0c;案由涉及滥用市场支配地位纠纷。公告显示&#xff0c;该案件计划…

[css]切角

使用css实现一个切角的功能&#xff0c;有以下几种方案&#xff1a; <div class"box"></div>方案一&#xff1a;linear-gradient linear-gradient配合backgroud-image可以实现背景渐变的效果。linear-gradient的渐变过渡区的占比是总的空间&#xff08;高…

分享一个可以测试离线服务器性能的脚本

在日常运维工作中&#xff0c;经常会遇到系统性能莫名跟不上业务需求的情况&#xff1a;服务器响应变慢、应用加载卡顿、资源占用异常飙升等问题频繁出现&#xff0c;却难以快速问题根源究竟在CPU过载、内存泄漏、磁盘I/O阻塞还是网络带宽瓶颈。这种时候&#xff0c;特别需要一…

Python Pandas.unique函数解析与实战教程

Python Pandas.unique 函数解析与实战教程 摘要 本文章旨在全面地解析 pandas 库中的 unique 函数。pandas.unique 是一个用于从一维数组型(array-like)对象中提取唯一值的高效工具。我们将从其核心功能、函数签名、参数详解、返回值类型,到关键行为特性(如顺序保留、缺失…