Go项目并发写入异常:程序提前退出,Goroutine未正常工作
问题分析与解决方案
这问题我之前做批量文件写入时也踩过一模一样的坑!核心原因是你的主goroutine没等所有负责写文件的子goroutine执行完就直接退出了——Go程序只要主goroutine结束,不管其他子goroutine有没有完成任务,都会直接终止整个进程。这就导致部分子goroutine还没来得及把内容写入文件,甚至连defer file.Close()都没执行,自然会出现文件已创建但内容为空的情况。
另外还有个容易忽略的细节:你直接把循环里的variable和values传给goroutine的话,因为Go循环变量是复用的,多个goroutine可能会拿到同一个变量的最终值,导致写入的内容和预期不符(不过你说串行正常、并发时是空文件,这个可能不是本次的主要问题,但还是要一起修正)。
修复步骤
我们可以用sync.WaitGroup来实现goroutine之间的同步,确保所有写入操作都完成后,主程序再退出:
- 引入
sync包,声明一个WaitGroup实例 - 启动每个写文件goroutine前,调用
wg.Add(1)标记有一个任务待完成 - 在goroutine内部,用
defer wg.Done()标记任务完成 - 所有goroutine启动后,调用
wg.Wait()等待全部任务结束
修改后的代码示例
主函数部分:
import "sync" // ... 其他业务代码 ... var wg sync.WaitGroup for variable, values := range varValMap { wg.Add(1) // 用匿名函数捕获循环变量的副本,避免goroutine拿到过期值 go func(v string, vals []string) { defer wg.Done() writeCsv(v, vals) }(variable, values) } // 等待所有写文件goroutine完成后再继续 wg.Wait()
writeCsv函数可保持不变:
func writeCsv(variable string, values []string) { fName := variable + ".csv" fPath := filepath.Join(xmlFolder, "_varval", fName) file, err := os.Create(fPath) check(err) defer file.Close() for _, value := range values { fmt.Fprintf(file, value + "\n") } }
为什么串行执行正常?
因为串行时,主goroutine会逐个执行writeCsv函数,每个函数都完整执行完(包括文件写入、关闭)才会进入下一次循环,整个程序只有所有文件都写完才会退出,自然不会出现内容丢失的情况。
内容的提问来源于stack exchange,提问作者Milos Grujic
相关产品推荐
相关产品推荐

