使用Golang Mutex锁时文件写入不符合预期的问题排查
问题排查与修复方案
常见错误点及对应解决方法
1. Mutex未全局共享(核心问题)
如果每个worker goroutine都初始化了独立的sync.Mutex实例,锁完全起不到互斥作用——多个goroutine各自持有自己的锁,依然会同时写入文件,导致数据被覆盖。
修复方式:确保所有worker共享同一个Mutex实例,通过指针传递或全局变量(全局变量需注意初始化时机)。
2. 文件打开模式错误
如果在每个worker中使用os.Create()打开输出文件,该函数会截断已有文件(清空内容后写入),哪怕加了锁,后执行的goroutine打开文件时会清空之前的写入内容,最终只剩最后一条数据。
修复方式:使用os.OpenFile()指定正确的打开模式:
f, err := os.OpenFile("output.txt", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) if err != nil { log.Fatal(err) } defer f.Close()
os.O_APPEND:以追加模式写入os.O_WRONLY:只写模式os.O_CREATE:文件不存在时创建
3. 未等待所有goroutine执行完成
如果主程序启动goroutine后直接退出,会导致部分worker还没完成写入就被终止,最终输出数据不全。
修复方式:使用sync.WaitGroup等待所有worker结束:
var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go func(workerID int) { defer wg.Done() // worker逻辑 }(i) } wg.Wait()
完整修正示例代码
package main import ( "encoding/json" "log" "os" "sync" ) // 假设API响应结构体 type APIResponse struct { Data string `json:"data"` } func main() { var ( mutex sync.Mutex wg sync.WaitGroup workerNum = 10 ) // 预打开输出文件(避免每个worker重复打开) outputFile, err := os.OpenFile("output.txt", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) if err != nil { log.Fatalf("无法打开输出文件: %v", err) } defer outputFile.Close() for i := 0; i < workerNum; i++ { wg.Add(1) go func(workerID int) { defer wg.Done() // 模拟从JSON文件读取API响应 jsonData := []byte(`{"data": "worker_` + string(rune(workerID+48)) + `_response"}`) var resp APIResponse if err := json.Unmarshal(jsonData, &resp); err != nil { log.Printf("worker %d: 解析JSON失败: %v", workerID, err) return } // 加锁保护文件写入 mutex.Lock() defer mutex.Unlock() // 写入数据到文件 if _, err := outputFile.WriteString(resp.Data + "\n"); err != nil { log.Printf("worker %d: 写入文件失败: %v", workerID, err) } }(i) } wg.Wait() log.Println("所有worker执行完成") }
关键注意事项
- 尽量提前打开输出文件,避免每个worker重复打开/关闭文件,减少IO开销和潜在问题。
- Mutex的
Lock()和Unlock()必须成对出现,使用defer mutex.Unlock()可以避免因panic导致锁无法释放。 - 如果worker需要处理大量文件读取/写入,可将文件写入逻辑封装成独立函数,确保锁的作用域覆盖整个写入流程。
内容的提问来源于stack exchange,提问作者imharjyotbagga
相关产品推荐
相关产品推荐

