for range ch 必须等通道关闭才退出,因它持续接收而非仅读缓冲区;未关闭时即使发送goroutine已退,仍会阻塞等待关闭信号,导致死锁或文件写入不全。

用 range 遍历 channel 写文件,但必须注意关闭时机
通道未关闭时,range 会永远阻塞等待新数据,程序卡住不动。这不是 bug,是 Go 的设计行为——range ch 等价于“等 channel 关闭后才退出”。所以写文件前,得确保生产者明确调用 close(ch),或用带超时/计数的退出机制。
常见错误现象:range 不退出、文件只写了一半、goroutine 泄漏。
- 如果生产者是异步 goroutine,推荐用
sync.WaitGroup+close()配合:生产完所有数据再关 channel - 如果数据流无明确终点(如日志推送),改用
for+select+ok判断,避免依赖关闭 - 写文件建议用
bufio.Writer包裹*os.File,减少系统调用次数
用 for + 持续读取,适合长周期数据流
当 channel 可能长期不关闭(比如接收传感器数据、消息队列消费),不能依赖 range。要用显式接收 + ok 判断是否已关闭,同时留出中断出口。
示例逻辑:
for {
data, ok :=
-
ok为 false 仅表示 channel 已关闭且缓冲区为空,不是错误信号 - 每次写完调用
Flush(),否则bufio.Writer可能缓存数据不落盘 - 若需实时性,可设
bufio.NewWriterSize(file, 1)强制单字节刷写(性能差,慎用)
并发写文件要加锁,或让单一 goroutine 负责写入
多个 goroutine 直接往同一个 *os.File 写,会出现内容错乱、覆盖、甚至 short write 错误。Go 的文件写入不是并发安全的。
- 最稳妥方式:只开一个 goroutine 专门从 channel 读 + 写文件,其他 goroutine 只负责发数据到 channel
- 避免用
sync.Mutex包裹WriteString—— 锁粒度太细,反而因频繁争抢降低吞吐 - 如果必须多写入源,考虑先写入带缓冲的 channel(如
ch := make(chan string, 1024)),再由单个消费者串行落盘
注意 os.O_APPEND 和文件截断问题
打开文件时没加 os.O_APPEND,多次运行程序会导致新数据覆盖旧内容。但即使加了,也要小心:如果程序崩溃重启,可能重复写入部分数据。
- 推荐打开方式:
os.OpenFile("log.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) - 不要用
os.Truncate()或os.Remove()清空文件——这会破坏正在写的句柄,导致write: bad file descriptor - 如需轮转日志,应新建文件并原子替换(如写到
log.txt.tmp后os.Rename()),而非清空原文件
WaitGroup 或 context 确保消费者真正完成写入再退出。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











