能,但只在单进程内有效;sync.mutex可防护goroutine间竞态,需锁住全部写路径、避免锁内耗时操作,并确保锁变量作用域正确。

并发写同一文件时,sync.Mutex 能不能用?
能,但只在单进程内有效——这是最关键的边界条件。如果你的程序只会启动一个进程(比如一个常驻服务),所有写操作都来自它的 goroutine,那 sync.Mutex 就是最轻量、最可控的选择。
常见错误现象包括:go run -race 报出 Write at 0x... by goroutine N,日志行被截断或拼接,文件末尾出现空字节或重复块。这些基本都是没锁住全部写路径导致的。
- 锁对象必须是包级或结构体字段级变量,不能在函数里声明新
sync.Mutex{},否则每个 goroutine 拿到的是不同锁 - 必须用
defer mu.Unlock()配合mu.Lock(),漏掉 defer 或提前 return 容易死锁 - 锁内只做纯
Write(),不要做fmt.Sprintf、json.Marshal、time.Now().Format()等耗时操作——这些应提前完成,只把最终[]byte交进去 - 如果一次写入需要多次
Write()(比如 header + body + footer),必须全部包在同一个Lock()/Unlock()块里,不能分段加锁
syscall.Flock 在 Windows 上为什么总 panic?
因为 Windows 根本不支持 flock(2) 系统调用,syscall.Flock 调用会直接返回 ENOSYS 或类似错误。这不是你代码写错了,是操作系统级不兼容。
它只在 Linux/macOS 可用;Windows 下必须换方案。即使你加了 LOCK_NB(非阻塞标志),报错类型仍是“系统不支持”,而不是“锁被占用”。
-
LOCK_EX是独占锁,LOCK_SH是共享锁,但在 Windows 上二者都无效 - 跨平台替代方案:用
github.com/gofrs/flock,它在 Windows 下自动 fallback 到CreateFile+FILE_SHARE_NONE模拟锁行为 - 锁文件路径建议独立于业务文件,比如目标是
data.json,就锁data.json.lock,权限设为0600
多进程场景下,只加 sync.Mutex 还够不够?
完全不够。只要启动两个独立的 ./myapp 进程,sync.Mutex 就彻底失效——它只管本进程内的 goroutine,不管其他进程。
典型多进程场景包括:cron 每分钟拉起一次脚本、K8s 多副本部署、日志轮转工具与主程序同时操作同一日志文件。
- 必须用系统级文件锁,如
flock(Linux/macOS)或gofrs/flock(跨平台) -
os.O_APPEND只保证单次Write()原子追加,但不防多进程同时写——仍可能两行内容挤在同一行 - 混用风险极高:只加
sync.Mutex却部署多个实例,等于没锁 - 第三方库推荐
gofrs/flock:初始化用flock.New("/path/to/lockfile"),阻塞获取用Lock(),非阻塞用TryLock(),释放必须显式Unlock()
高频小写入(如 Web 请求日志)该选什么锁?
别硬扛 sync.Mutex 排队模型。它会让所有写请求串行,吞吐受限,尤其在 QPS 上千时明显卡顿。
更合理的做法是引入缓冲+异步落盘,把锁粒度从“每次写”降到“批量刷盘”:
- 用
bufio.Writer缓冲写入,WriteString后先不Flush,攒够一定量或超时再刷 - 开一个单独 goroutine 专门负责
Write+Flush,用chan []byte接收日志消息 - 主流程只往 channel 发送,无锁、无阻塞,channel 自带同步语义
- 若仍需强一致性(如审计日志),可在刷盘前对 channel 写入加
sync.Mutex,但锁住的只是内存拷贝,不是 I/O
真正容易被忽略的是锁的生命周期:别为了“保险”一直拿着锁做网络请求、sleep 或复杂计算——这些全得移出临界区,否则并发吞吐直接归零。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











