多个goroutine并发调用os.WriteFile会覆盖彼此内容,因其原子性仅限单次调用;写同一文件需加锁或改用其他方案;os.File.Write非线程安全,须显式同步;bufio.Writer不支持并发共享,应按goroutine隔离或用sync.Pool复用。

多个 goroutine 直接 os.WriteFile 会覆盖彼此内容
Go 标准库的 os.WriteFile 是原子写入(先写临时文件再 rename),但「原子」只保证单次调用不被中断,不保证多 goroutine 并发调用时的顺序或互斥。如果 10 个 goroutine 同时对同一路径调用 os.WriteFile,最终文件内容大概率是最后一次写入的结果——前面 9 次全被覆盖。
- 这不是 bug,是设计使然:它没义务帮你同步
- 适用场景仅限「每个 goroutine 写不同文件」,比如日志按 ID 分片:
log_user_123.json、log_user_456.json - 若必须写同一文件(如汇总统计),得自己加锁或换方案
用 os.OpenFile + file.Write 时必须显式加锁
打开一个共享 *os.File 后,并发调用其 Write 方法会导致数据错乱甚至 panic(底层 write(2) 系统调用不是线程安全的)。Go 的 os.File 本身不带内部锁,Write 只是转发系统调用。
- 最简方式:用
sync.Mutex包裹file.Write调用 - 注意别锁住
file.Close—— 关闭后其他 goroutine 再 Write 会 panic:write: bad file descriptor - 避免在循环里反复
OpenFile(..., os.O_WRONLY|os.O_APPEND):开销大,且O_APPEND在多数文件系统上能保证追加原子性,但仍有极小概率因内核缓冲导致行交错(尤其写入非完整行时)
bufio.Writer 和 sync.Pool 配合能缓解频繁 I/O 压力
直接对 *os.File 频繁 Write 小数据(比如每条日志一行),系统调用开销高。用 bufio.Writer 缓冲后再刷盘更高效,但要注意:它不是并发安全的,不能多个 goroutine 共享同一个实例。
牛NIUCMS本地O2O系统是一个以php+mysql进行开发的o2o网站系统。NIUCMS是一款强大的网站管理系统。支持智慧城市、智慧小区、智慧乡村、本地生活门户、本地O2O平台的构建。请注意以下几点:1、这套源码必须要服务器支持伪静态,是支持.htaccess规则的伪静态,一般Apache服务器支持,别搞的下载回去以后说什么缺 少文件,其实源码并非缺少文件。2、这套源码请在php 5.4环境下
- 推荐做法:每个 goroutine 自己配一个
bufio.Writer,写完调Flush - 若创建太频繁,可用
sync.Pool复用bufio.Writer实例(注意:Pool.Get返回的 writer 缓冲区可能非空,务必重置:wr.Reset(file)) - 别省略
Flush:不调用就可能丢数据;也别在 defer 里无条件Flush——万一 writer 还没写任何内容,Flush会触发一次空系统调用
写入失败时,io.EOF 不代表磁盘满,syscall.ENOSPC 才是
并发写文件出错时,错误类型比错误字符串更可靠。常见误区是检查 err == io.EOF,但它只在读取到流末尾时出现,写入过程根本不会返回这个值。
立即学习“go语言免费学习笔记(深入)”;
- 磁盘满的真实信号是
syscall.Errno == syscall.ENOSPC(需导入golang.org/x/sys/unix或用errors.Is(err, syscall.ENOSPC)) - 权限不足对应
syscall.EACCES,路径不存在是syscall.ENOENT - 并发下还可能出现
syscall.EAGAIN或syscall.EWOULDBLOCK(尤其写 pipe 或 socket),但普通文件少见
O_SYNC 开标志又忘了处理对应性能暴跌,或者依赖 fsync 却没检查它的返回值。这些细节一旦漏掉,在高负载下才会暴露。









