io.Pipe 不是线程安全的并发通道,仅支持单读单写或单写多读,写入端必须调用 Close 或 CloseWithError 才能让读取端退出阻塞,否则易死锁。
Go 的
不是线程安全的“并发读写通道”
它本质是一对绑定的
和
,底层共享一个带锁的缓冲区,但**不支持任意 goroutine 同时读+写**——写入方未关闭前,读取方可能阻塞;若双方都未按约定控制生命周期,极易死锁。
常见错误现象:
,尤其在没用
或忘了用
等待写入完成时高频出现。
只适合「单写多读」或「单读单写」这种有明确流向的场景,比如日志转发、HTTP 响应流式生成
不要把它当
用,也不要用在需要高吞吐或低延迟的管道通信中
写入端必须调用
(或
)才能让读取端退出
阻塞
如果写入端 panic 未 close,读取端会永远卡住 —— 生产环境务必用
+
包裹
的正确启动姿势:谁创建、谁 close、谁负责错误传播
管道两端生命周期必须由同一方协调,典型模式是封装成函数返回
,把写入逻辑藏在 goroutine 内部。
示例:构造一个按需生成 JSON 流的
立即学习
“
go语言免费学习笔记(深入)
”;
go语言参考手册 中文CHM版
Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。本文给大家带来Go参考手册,需要的可以来下载! Go是从2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持开发,后来还加入了Ian Lance Taylor, Russ Cox等人,并最终于2009年11月开源,在2012年早些时候发布了Go 1稳定版本。现在Go的开发已经是完全开放的,并且拥有一个活跃的社区。 Go 语言特色 简洁、快速、安全 并行、有趣、开源 内存管理、v数组安全、编译
下载
写入 goroutine 必须
,否则 reader 永远等不到 EOF
出错时用
,这样 reader 的
会立即返回该 err,而不是静默卡住
reader 端拿到的是
,自己负责
—— 这会触发内部 cleanup,但不会影响 writer 已结束的流程
不要在外部直接调用
,除非你完全掌控 writer goroutine 的状态
替代方案比
更靠谱的三个时机
多数人想用
其实是为了解耦生产/消费,但往往有更稳的选择。
需要缓冲且可控容量 → 用
或
,写完再传
给 reader,零 goroutine 开销
需要多消费者共享数据 → 改用
+ 多个
,避免 pipe 的单读限制
真正要流式处理 HTTP/CLI 输出 → 直接用
(系统级)或
,或者上
自行调度,
在这里只是徒增死锁风险
它的存在意义很窄:当你必须满足某个只接受
接口的函数签名,又不想提前把全部数据 load 到内存时,才值得动它。
调试
死锁的两个关键信号
一旦卡住,别急着加 log,先看这两个地方。
用
查 goroutine 状态:如果看到 reader 卡在
,writer 卡在
,基本就是双方都没 close,或 writer panic 了没 recover
检查 writer 是否在循环中调用了阻塞操作(如未设 timeout 的 HTTP 请求、数据库查询),导致迟迟不走到
临时加一行
在 writer 循环内,确认是否真执行到了末尾
最常被忽略的是:writer goroutine 被其他错误提前终止,而
根本没机会执行 —— 所以务必在所有出口路径上保证 close,包括
、
、
前。
io.Pipeio.Readerio.Writerfatal error: all goroutines are asleep - deadlockclose()sync.WaitGroupchan []bytew.Close()w.CloseWithError(err)Readdefer w.Close()recoverio.Pipeio.ReadCloserio.ReadCloserfunc jsonStream() io.ReadCloser {
r, w := io.Pipe()
go func() {
defer w.Close() // 关键:确保无论成功失败都 close
enc := json.NewEncoder(w)
for _, v := range []int{1, 2, 3} {
if err := enc.Encode(v); err != nil {
w.CloseWithError(err) // 错误要传给 reader 端
return
}
}
}()
return r
}defer w.Close()w.CloseWithError(err)Readio.ReadCloserClose()w.Close()io.Pipeio.Pipebytes.Bufferstrings.Builder.Bytes()io.MultiReaderbytes.NewReaderos.Pipe()net.Connchan []byteio.Pipeio.Readerio.Pipego tool trace(*pipeReader).Read(*pipeWriter).Writew.Close()log.Printf("writing %v", v)defer w.Close()returnpanicos.Exit