news 2026/4/23 12:58:10

每日八股——Go高频(3)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
每日八股——Go高频(3)

Channel的底层结构与原理

channel 是一种用于在协程(goroutine)之间进行通信的机制。它的底层原理主要基于操作系统提供的同步原语(如互斥锁和条件变量)以及 Go 运行时(runtime)的调度机制。 Go 的 channel 在运行时对应一个结构体runtime.hchan, 核心字段为:

typehchanstruct{qcountuint// 当前队列中元素数量dataqsizuint// 环形队列容量(有缓冲区才用)buf unsafe.Pointer// 环形缓冲区(底层是数组)elemsizeuint16// 每个元素大小closeduint32// 是否已关闭sendxuint// 环形队列写位置recvxuint// 环形队列读位置recvq waitq// 因为接收而阻塞的 goroutine 队列sendq waitq// 因为发送而阻塞的 goroutine 队列lock mutex// 保护 hchan 的互斥锁}

channel有两种主要的对象:缓冲区buf和等待队列recvq与sendq, 等待队列里面是 sudog 对象(不是 goroutine 本身,而是 goroutine 在 channel 等待时的状态封装结构)。
带缓冲区的 channel 有一个固定大小的缓冲区(dataqsiz > 0),buf 是一个环形缓冲区。
无缓冲区的 channel 没有缓冲区(dataqsiz==0),即 buf 为空。

八股回答

Go 的 channel 底层是一个 hchan 对象,底层是一个结构体,通常称为 hchan。它的主要字段包括:

  • qcount:队列中元素的数量。
  • dataqsiz:队列的大小,即 channel 的缓冲区大小。
  • buf:缓冲区,用于存储 channel 中的元素。
  • elemsize:元素的大小(以字节为单位)。
  • closed:一个标志,表示 channel 是否已经关闭。
  • lock:互斥锁,用于保护 channel 的状态。
  • send 和 recv:等待队列,分别用于存储等待发送和接收的协程。

对于有缓冲的 channel,元素存放在环形队列里;对于无缓冲 channel,发送和接收 goroutine 必须同时到达形成同步点。
close 会唤醒所有阻塞的接收者,并让阻塞的发送者 panic。
发送到接收之间,以及 close 到接收关闭状态之间,都形成 happens-before 关系,是 Go 内存模型的基础同步保证。

happens-before关系:happens-before 是 Go 内存模型里定义的一种“先行发生”关系,用来说明在并发环境下,哪些操作一定在另一些操作之前对其他 goroutine 可见。写并发代码的时候,我们会用同步原语(sync.Mutex、sync.RWMutex、sync/atomic包里的操作、channel本身等 )来建立 happens-before,使得跨 goroutine 的读写有明确的顺序保证,否则就会产生 data race 和未定义行为。 (不需要会背,是写代码的一个准则,能理解就行,估计也不会问到这一层来)
听起来有点抽象,给大家举一个简单的例子:

不安全(没有 happens-before)varxintfuncf(){x=1}funcg(){fmt.Println(x)// 这里对 x 的读没有同步保证}funcmain(){gof()gog()}

● 没有锁,没用 channel,没用 atomic
● 两个 goroutine 对 x 的访问之间没有任何 happens-before 关系
● 结果:data race,行为未定义,go test -race 会报错

var(xintch=make(chanstruct{}))funcf(){x=1close(ch)// A}funcg(){<-ch// Bfmt.Println(x)}funcmain(){gof()gog()}

close(ch)发生在g()<-ch返回之前,并且保证close(ch)之前的所有写操作,在<-ch之后对g()都是可见的。
● 所以g()中看到的 x 必须是 1

Channel发送/接收数据过程

大局观:channel做两件事

1、存数据:缓冲区放数据或直接在goroutine之间拷贝
2、管排队:管理sendq与recvq,所有发送/接收操作都会先拿hchan.lock,在同一份状态上做判断,然后决定是直接完成还是把自己挂到阻塞队列里。

发送过程(ch<-)

1、检查+拿锁
● 先判断ch==nil:永远阻塞(拿锁前检查)
● 调用runtime的chansend,获取channel的互斥锁,
● 再判断closed==1:直接panic:send on closed channel(拿锁后检查)
2、 如果 recvq 非空 → 优先唤醒一个等待的接收者
● 从recvq里pop出一个等待的接收者sudog(本质上是一个带状态的goroutine),直接把要发送的值x拷贝到接收者提供的接收地址reviver buffer上(省了一次入队出队操作)
3、 如果recvq 为空 → 尝试写入缓冲区
● 如果缓冲区未满:将值写入缓冲区
● 如果缓冲区已满或无缓冲区:构造sudog(记录当前goroutine与待发送数据地址),将sudog加入sendq,释放hchan.lock,阻塞当前goroutine,等待接收方匹配唤醒.

接收过程(<-ch)

1、检查+拿锁

  • 先判断ch==nil:永远阻塞(拿锁前检查)
  • 调用runtime的chanrecv,获取channel的互斥锁,
  • 再判断closed==1且缓冲区为空:返回零值+ok=false(拿锁后检查)

2、如果sendq非空->优先唤醒一个等待的发送者

  • 如果是无缓冲区:从sendq里pop出一个等待的发送者sudog,唤醒发送者,将发送者提供的值拷贝给接收者。
  • 如果缓冲区满:从缓冲区取出值给接收者,唤醒等待的发送者把值插入缓冲。

3、 sendq 为空 → 尝试从缓冲区读取

  • 如果缓冲区非空 :取出缓冲区值,返回并释放锁
  • 如果缓冲区为空:
    • channel已关闭:返回零值+ok=false
    • channel未关闭:构造sudog,将sudog加入recvq,释放hchan.lock,阻塞当前goroutine,等待发送方匹配唤醒。


      在 Go runtime 里,sudog(常被理解为 “suspended goroutine” 的缩写)是一个用于把 goroutine 挂到同步原语等待队列上的节点结构。它不是 goroutine 本身,而是 goroutine 在“等待某件事发生”时,runtime 为其创建/复用的一段等待上下文。 如果不想面试官追问sudog的内容,可以直接说发送方接收方加入队列,不说sudog。


写给自己

感觉写的这几篇都是文字,排版挺密集的,可能没啥人愿意看的🤔要不要换一个排版和风格呢?但说到底还是要背的。或者要不要开第二个专题分享自己做hot100的思路和代码注释呢?🤔
算啦算啦~,慢慢来吧。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 7:52:12

BilibiliVideoDownload:跨平台B站视频离线下载完整指南

想要随时观看B站视频而不用担心网络问题&#xff1f;BilibiliVideoDownload正是你需要的解决方案。这款开源桌面应用让你能够轻松下载B站视频到本地&#xff0c;实现真正的离线观看体验。 【免费下载链接】BilibiliVideoDownload 项目地址: https://gitcode.com/gh_mirrors/…

作者头像 李华
网站建设 2026/4/23 7:52:44

阴阳师百鬼夜行效率提升终极指南:5个自动化技巧快速掌握

阴阳师百鬼夜行效率提升终极指南&#xff1a;5个自动化技巧快速掌握 【免费下载链接】OnmyojiAutoScript Onmyoji Auto Script | 阴阳师脚本 项目地址: https://gitcode.com/gh_mirrors/on/OnmyojiAutoScript 在阴阳师这款经典和风手游中&#xff0c;百鬼夜行是获取稀有…

作者头像 李华
网站建设 2026/4/23 7:50:37

大模型应用技术之多语言RAG【实战篇】

1. 环境准备 1.1 依赖安装 # 安装LlamaIndex核心包 pip install llama-index# 安装多语言相关依赖 pip install llama-index-embeddings-huggingface pip install sentence-transformers# 安装语言检测工具 pip install langdetect # 或 pip install lingua# 安装向量数据库&am…

作者头像 李华
网站建设 2026/4/23 7:52:28

代码随想录算法第四十二天| LeetCode188买卖股票的最佳时机Ⅳ、LeetCode309最佳买卖股票时机含冷冻期、LeetCode714买卖股票的最佳时机含手续费

LeetCode 188 买卖股票的最佳时机 Ⅳ 题目链接&#xff1a;188.买卖股票的最佳时机 Ⅳ 文档讲解&#xff1a;代码随想录 视频讲解&#xff1a;买卖股票的最佳时机 Ⅳ 思路与感想&#xff1a;这道题目虽然是道hard但是在做过了股票系列Ⅲ后立马就有思路直接秒了&#xff0c;跟Ⅲ…

作者头像 李华
网站建设 2026/4/23 7:48:38

TegraRcmGUI完整指南:5步轻松实现Switch Payload注入

TegraRcmGUI完整指南&#xff1a;5步轻松实现Switch Payload注入 【免费下载链接】TegraRcmGUI C GUI for TegraRcmSmash (Fuse Gele exploit for Nintendo Switch) 项目地址: https://gitcode.com/gh_mirrors/te/TegraRcmGUI TegraRcmGUI是一款专为Nintendo Switch设计…

作者头像 李华
网站建设 2026/4/23 9:16:16

数字时代,传统碟片的销量不减反增

你是从什么时候开始接触MP3的呢&#xff1f;笔者上大学时还在用1.4寸的磁盘写毕业论文&#xff0c;那时谁有一个64M的优盘&#xff0c;128M的优盘&#xff0c;老羡慕了&#xff0c;你没看错是M不是G&#xff0c;后来随着内存存储技术的发展&#xff0c;MP3开始上G大小了&#x…

作者头像 李华