使用io.Pipe易死锁,需goroutine+带缓冲channel;读取时须用n, err := reader.Read(buf)并处理n>0数据;复用buffer或sync.Pool避免频繁分配;传切片到channel前必须copy防内存错乱。

直接用 io.Pipe 接文件读写会死锁
它没缓冲、不背压、两端强绑定——pw.Write() 一调就卡住,除非另一端立刻 pr.Read()。常见错误是:主线程里 io.Copy(pw, file) 同步写,但下游还没启 goroutine 去读,整条链瞬间挂死;或者忘了 pw.Close(),下游永远等不到 io.EOF,协程泄漏。
真正能跑通大文件的流式读取,得靠显式 goroutine + 带缓冲 channel,比如 make(chan []byte, 16),再配 os.Open 和 io.Copy 或 bufio.Reader 组合使用。
从 os.Stdin 读管道输入必须检查 n
别写 _, err := reader.Read(buf) —— 这会丢掉实际读到的字节数 n,导致把未填充的垃圾内存当有效数据,甚至把 100MB 输入误读成几 GB。
- 必须用
n, err := reader.Read(buf),且每次读完先处理n > 0的数据 - 遇到
err == io.EOF时,只要n > 0就得立刻消费,否则末尾数据丢失 - 不要在循环里反复
make([]byte, size),改用固定大小 buffer 复用底层数组,或用sync.Pool管理
用 chan []byte 做分块流水线要防内存错乱
往 channel 里传 buf[:n] 是危险的:多个消费者可能同时修改同一块底层数组。必须拷贝:
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
立即学习“go语言免费学习笔记(深入)”;
data := make([]byte, n) copy(data, buf[:n]) ch <- data
缓冲区大小也关键:make(chan []byte, 16)(约 64KB)是较稳的选择——太小易频繁阻塞,太大吃内存。读 goroutine 结束前必须 defer close(ch),否则下游 range ch 永远卡住。
接入 csv.Reader 或 gzip.Writer 别绕弯
不要先把全部数据读进 bytes.Buffer 再喂给解析器——这等于放弃流式,内存爆表。正确做法是让解析器直接从流中拉数据:
- 上游是
chan []byte→ 启 goroutine 把数据写入io.PipeWriter - 下游用
csv.NewReader(pipeReader)直接消费 - 加压缩?用
gzip.NewWriter(outFile)作最终io.Writer,io.Copy(gzipWriter, pr)自动触发背压,上游自然慢下来
最常被忽略的是关闭顺序和错误传播:pw.Close() 必须在所有写操作完成后调,且只能调一次;写端出错要用 pw.CloseWithError(err),否则读端无法感知失败。

















