-
Go 中 exec.Command 的 Wait() 方法无限阻塞的解决方案
- 时间:2026-09-02 来源:小编 人气:
当使用 go 的 exec.command 执行 mysql 导入命令时,若未显式关闭 stdinpipe,wait() 会因标准输入未终止而永久阻塞;根本原因是 mysql 进程持续等待更多输入,直到 stdin 关闭才真正退出。
在 Go 中调用外部命令(如 mysql)进行数据导入时,常通过 exec.Command 启动进程,并借助 StdinPipe() 将数据流式写入。但一个易被忽视的关键点是:StdinPipe 返回的写入端必须显式关闭,否则子进程(如 mysql)无法感知输入结束,将一直阻塞在读取状态,导致后续 cmd.Wait() 永不返回。
上述代码的问题正在于此:虽然 io.WriteString(stdin, data) 成功写入了 SQL 内容,但 stdin 管道仍处于打开状态。MySQL 客户端默认行为是持续监听标准输入(例如支持多语句、交互式输入),只有当 stdin 被关闭时,它才会完成解析并正常退出。
✅ 正确做法是在写入完成后立即调用 stdin.Close():
1 2 3 4 |
|
⚠️ 注意事项:
stdin.Close()必须在cmd.Start()之后、cmd.Wait()之前调用;- 若写入数据量较大,建议使用
io.Copy替代io.WriteString,并确保在Copy完成后再关闭(io.Copy本身不会自动关闭管道); - 不要依赖
defer stdin.Close()—— 它可能过早关闭,导致数据未写完进程就退出; - 可添加超时机制增强健壮性,例如用
context.WithTimeout包裹cmd.Wait(); - 始终检查
io.WriteString或io.Copy的返回错误,避免静默失败。
总结:exec.Cmd.StdinPipe() 的设计逻辑是“管道生命周期由用户控制”,其文档明确指出:“调用方只需调用 Close 即可提前关闭管道”——尤其当被调用程序(如 mysql、cat、sort)依赖 EOF 判断输入终止时,显式关闭 stdin 是 Wait() 正常返回的必要前提。
推荐文章
-
这个暑期,世界机器人运动会的画风有点混搭。冰丝带里,穿武术服的机器人扎着马步,一招“白鹤亮翅”推得有板有眼;北京消防战勤...2026-08-28