
本文介绍使用 Go 语言中的 sync.RWMutex 实现对同一文件的并发读写安全控制,确保多 goroutine 环境下读操作不会因写入中断而读到脏数据或引发 panic,同时最大化读并发性能。
本文介绍使用 go 语言中的 `sync.rwmutex` 实现对同一文件的并发读写安全控制,确保多 goroutine 环境下读操作不会因写入中断而读到脏数据或引发 panic,同时最大化读并发性能。
在高并发 Web 服务中,常有场景需要持续向一个日志或状态文件追加写入(如每秒数十次请求写入),同时又需不定期读取该文件内容进行解析、监控或导出。此时若直接无协调地读写同一文件,极易导致数据竞争、读取截断、I/O 错误甚至 panic(例如 read: file closed 或 invalid memory address)。Go 标准库提供的 sync.RWMutex 是解决此类问题的经典模式——它允许多个读者并发访问,但写操作独占临界区。
✅ 正确用法:读写分离锁机制
RWMutex 提供两种锁定方式:
- RLock() / RUnlock():允许多个 goroutine 同时持有读锁,只要没有写锁存在;
- Lock() / Unlock():写锁为排他锁,会阻塞所有新读锁和写锁,直到当前写操作完成。
这意味着:
- 多个 readFile() 可并行执行,互不阻塞;
- writeFile() 执行时,新来的读请求会等待写完成;
- 写入过程中若有大量读请求积压,RWMutex 仍能保证最终一致性,且不会死锁。
? 完整示例(含文件 I/O)
package main
import (
"fmt"
"io"
"os"
"sync"
)
var fileMutex = new(sync.RWMutex)
const filename = "data.log"
// readFile 安全读取整个文件内容
func readFile() ([]byte, error) {
fileMutex.RLock()
defer fileMutex.RUnlock()
data, err := os.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("failed to read file: %w", err)
}
return data, nil
}
// writeFile 安全追加写入一行
func writeFile(line string) error {
fileMutex.Lock()
defer fileMutex.Unlock()
f, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return fmt.Errorf("failed to open file for write: %w", err)
}
defer f.Close()
_, err = io.WriteString(f, line+"\n")
if err != nil {
return fmt.Errorf("failed to write line: %w", err)
}
return nil
}
⚠️ 关键注意事项
- 锁粒度要匹配操作范围:RWMutex 保护的是“对文件的访问行为”,而非文件内容本身。它不能防止外部进程(如 rm、cp)干扰,也不替代原子写入(如 os.WriteFile 替代 os.Open+Write 更安全)。
- 避免在锁内做耗时操作:如网络请求、复杂解析等应放在 RUnlock() 之后,否则会严重拖慢并发读写效率。
- 推荐配合 os.ReadFile / os.WriteFile 使用:它们是原子性封装,比手动 Open + Write + Close 更健壮;若需流式读写(如大文件),请确保 Read/Write 调用也在锁保护范围内。
- 跨进程不适用:RWMutex 仅作用于当前进程内 goroutine。若需多进程协同,应改用文件系统级锁(如 syscall.Flock)或外部协调服务(如 Redis 分布式锁)。
✅ 总结
sync.RWMutex 是 Go 中实现单进程内文件读写并发安全的轻量、高效方案。它通过读写分离机制,在保障数据一致性的同时,显著提升读密集型场景的吞吐能力。只要严格遵循「读前 RLock、写前 Lock」原则,并将实际 I/O 操作置于锁保护之下,即可稳健支撑高频写+低频读的典型运维与监控需求。











