Go 37:一个阻塞接收从源码到任务终态
上篇按任务 ID 对齐了等待的事件与栈。仅凭 [chan receive] 四个字还不能回答:什么操作让接收方返回、返回时 ok 是多少、服务何时才可以宣布 worker 退出?这里选累计任务服务里一个阻塞接收,把公开语义、冻结版源码和正反两次受控实验接在一起。完整 源码证据包、SHA-256与独立复跑步骤附在同名素材目录。
公开契约先于调度实现
Go 1.27.1 随附语言规范的 Receive_operator 指出,接收表达式在没有可取值时会阻塞;从已经关闭并且排空的 channel 接收会立即返回元素类型零值。Close 章节与 go doc builtin.close 还规定了二值接收:关闭且排空后 ok=false。未关闭时与发送者配对接收一个值,ok=true。返回值先按冻结规范判定,再由测试确认;goroutine 栈用于识别阻塞点。
新增加的 TestChannelWakeAndWorkerExit 将当前 HTTP handler 接收的 task[0] 停在分析器的无缓冲 <-release;先查状态仍为 RUNNING,再让 task[1] 进入唯一队列槽,第三条必须返回 503 且 task[2] 不存在。测试真正拍到包含分析器的 [chan receive] 栈之后,发起服务 Close,但释放前不得宣称它已等待 worker 退出。测试既比较关闭 release channel,也比较向仍打开的 channel 发送一个值:
| 放行方法 | 接收方返回的 ok |
Close 确认后两条终态 |
|---|---|---|
close(release) |
false |
task[0] SUCCEEDED words=2,task[1] SUCCEEDED words=1 |
release <- struct{}{} |
true |
task[0] SUCCEEDED words=2,task[1] SUCCEEDED words=1 |
关闭的是实验闸门而不是任务的取消 context;ok=false 只说明当前接收看到 channel 已关闭,不表示任务应该 CANCELED。任务的正常结果来自原 AnalyzeContext 和原任务存储的结果提交;Close 的返回需要 worker 真正退出。收到 POST 202 只说明已接受,不能替代这两个终态。
固定版源码中接收者怎样再次可运行
源码 tag go1.27.1 的镜像查询完整 SHA 是 862c888e612ac346c7c4d99c9392bdfd265f33b0。本轮另核对安装版 src/runtime/chan.go 的 SHA-256 为 dbe8d02ebb1b554b28263c8f34747c0b6d796b3e7040a2f6a625c472e9ce9a42、src/runtime/proc.go 为 40d15ce858cf058cff92606001a29ee1689db88c121a2f0c283163a0d64a7375;前者是 Git 标识,后两个是实际文件哈希。官方 Gitiles 与镜像 SHA 的独立交叉核验尚未完成,不写成已验证。
调用链的入口在 runtime/chan.go:chanrecv2→chanrecv。未就绪时,chanrecv 将接收方挂入 recvq 并通过 runtime/proc.go:gopark 停放 G。当前版本的发送方走 chansend→send,把值交给等待中的接收方,将 sudog.success 置为 true 并调用 goready;关闭方 closechan 把 closed 置位,从接收队列取出等待者,将 success 置为 false,解锁后调用 goready。proc.go:goready→ready 将 G 从 _Gwaiting 改为 _Grunnable 并放到可运行队列;唤醒不等于立刻得到 CPU。被唤醒的 chanrecv 读取 mysg.success,返回给二值接收形式,接下来才会执行分析器剩余代码。
这些文件、实际符号、行号和原始片段都在 writing-plans/go-language/evidence/37/source-*;它们解释这个已冻结实现为何符合观察到的 ok,不是让语言规范从此承诺 goready 的名字、run queue 结构或时间片。十轮正反复跑中,每次都要在解除闸门之前看到阻塞、之后看到两条 SUCCEEDED;只截图一行 [chan receive] 无法证明关闭流程。
练习:① close(release) 后接收 ok=false,任务是否应标为 CANCELED?不应该;这个 release 是实验同步闸门,不是服务取消请求,更不是 worker 退出确认。② goready 被调用,能立即向 HTTP 客户端返回业务完成吗?不能,G 还要得到运行机会、分析器要返回并提交结果,服务等待协议还未结束。③ 若取消与正常完成竞争怎么办?回到此前任务状态提交点选择唯一终态,不能用本篇关闭闸门的两个测试冒称已覆盖所有交错。
真实 PostgreSQL 16.15 的累计 SQL 回归、两包测试、race 的实际路径和旧 CLI 正反都在本次独立解包复跑中保留;本篇闸门实验本身没有进入 SQL,数据库提交也不代表外部副作用完成。race 无报告只覆盖执行路径,远程网络、其他平台、含 37 的全站构建与发布仍 NOT_RUN。下一篇从干净目录构建可交付程序,并限制诊断端点的监听范围。
参考资料
- Go 1.27.1 语言规范:接收及关闭;冻结版
doc/go_spec.html片段与go doc builtin.close原文在writing-plans/go-language/evidence/37/。 - Go 1.27.1
runtime/chan.go和runtime/proc.go,固定源码 SHA、文件哈希和符号见同一证据目录。






