
本文介绍使用 go 语言中的 sync.rwmutex 实现对同一文件的并发读写安全控制,确保读操作不会阻塞写操作(反之亦然),同时避免数据竞争和脏读问题。
本文介绍使用 go 语言中的 sync.rwmutex 实现对同一文件的并发读写安全控制,确保读操作不会阻塞写操作(反之亦然),同时避免数据竞争和脏读问题。
在高并发 Web 服务中,常需将请求日志、指标或状态持续追加写入单个文件(如 access.log 或 state.json),同时又需定时或按需解析该文件进行监控、归档或分析。此时若直接用 os.Open 读取而无同步机制,极易遭遇竞态条件:读取可能中断于写入中途,导致解析失败(如 JSON 格式错误、截断内容)或读到不一致的中间状态。
标准且推荐的解决方案是引入 读写互斥锁(sync.RWMutex),它允许多个读操作并行,但写操作独占——完美契合“高频写 + 低频读”的典型场景:
- ✅ RLock() / RUnlock():允许多个 goroutine 同时读取,互不阻塞;
- ✅ Lock() / Unlock():写操作时自动阻塞所有新读/写请求,确保原子性;
- ✅ 读不阻塞读,写阻塞读与写,兼顾性能与安全性。
以下为完整可运行示例(含错误处理与实际 I/O):
package main
import (
"fmt"
"io"
"os"
"sync"
)
var fileMutex = new(sync.RWMutex)
const filename = "data.log"
// 安全写入:追加一行日志
func writeFile(data 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 writing: %w", err)
}
defer f.Close()
_, err = io.WriteString(f, data+"\n")
return err
}
// 安全读取:返回当前全部内容(适合中小文件)
func readFile() ([]byte, error) {
fileMutex.RLock()
defer fileMutex.RUnlock()
return os.ReadFile(filename) // 等价于 os.Open + io.ReadAll
}
// 使用示例
func main() {
// 并发写入
go func() { _ = writeFile("request-1") }()
go func() { _ = writeFile("request-2") }()
// 安全读取(即使写入中也会等待完成)
content, err := readFile()
if err != nil {
panic(err)
}
fmt.Printf("Current content:\n%s", string(content))
}
⚠️ 关键注意事项:
- 避免长时持有锁:readFile() 中不应在 RLock() 内执行耗时解析(如复杂 JSON 解码),建议先 ReadFile() 获取字节切片,再解锁后处理;
- 文件大小限制:RWMutex 仅保障访问顺序,不解决大文件读写性能瓶颈;若日志量极大,应考虑轮转(log rotation)、分片或专用存储(如 SQLite、BoltDB);
- 进程级隔离:此方案仅适用于同一进程内多 goroutine 的并发控制;跨进程读写需额外机制(如文件锁 syscall.Flock 或消息队列);
- 初始化一致性:首次创建文件时,建议在程序启动时用 Lock() 初始化空文件,防止竞态创建。
总结:sync.RWMutex 是 Go 中平衡读写效率与线程安全的轻量级利器。只要严格遵循“读用 RLock、写用 Lock、及时 defer Unlock”三原则,即可在不阻塞业务写入的前提下,安全可靠地读取动态更新的文件内容。











