Golang主程序CPU负载在子程序关闭时上升的原因分析
Golang主程序CPU负载在子程序关闭时上升的原因分析 我有一个网络服务,在收到请求时会在后台运行一个命令:
cmd := exec.Command(instPath, settings.ExecWorkingDir+settings.ExecFileEnvPath, settings.ExecWorkingDir+settings.ExecFileLogPath, ":"+freeport, id)
if err := cmd.Start(); err != nil {
return nil, err
}
e.Pid = cmd.Process.Pid
go func() {
_ = cmd.Wait()
}()
go e.loop()
return e, nil
func (e *Exec) loop() {
defer e.stop()
for e.Work {
p, err := os.FindProcess(e.Pid)
if err != nil {
e.Err = true
e.Block = true
return
} else {
err := p.Signal(syscall.Signal(0))
if err != nil {
e.Err = true
e.Block = true
return
}
}
}
}
循环方法检查子程序是否正在运行,如果子程序执行完成,就会退出循环并触发停止方法。这个方法工作正常。但是当子程序被终止时,网络服务(主程序)的CPU负载会上升300%。
我使用的操作系统是Linux,Elementary OS。
更多关于Golang主程序CPU负载在子程序关闭时上升的原因分析的实战教程也可以访问 https://www.itying.com/category-94-b0.html
你的代码结构不太完整,变量e是什么类型?这个循环是个非常紧凑的循环。或许你可以在每次迭代时加入一些休眠时间:
time.Sleep(100 * time.Millisecond)
另外,为什么你要通过进程ID来查找进程并检查它是否结束,而不是直接使用cmd.Wait()方法呢?
更多关于Golang主程序CPU负载在子程序关闭时上升的原因分析的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
再次强调,如果我不使用 wait() 方法,当我终止子进程时会出现“进程僵死”状态。这意味着进程变成了僵尸进程,它虽然已被终止,但主进程仍在等待子进程的某些操作。
我已经尝试为每次迭代设置延迟,但这并没有帮助。
结构体E没有特殊含义;我们假设它用于保存PID的值。我无法使用Wait()方法,因为我需要返回一些数据,而且我运行的命令可能会永久运行,这是一种守护进程,Wait()方法会等待子程序完成。
这个问题通常是由于在 loop() 函数中缺少适当的延迟或休眠机制导致的。当子进程终止后,e.Work 保持为 true,循环会以极高的频率持续检查进程状态,造成CPU空转。
以下是修复后的代码示例:
func (e *Exec) loop() {
defer e.stop()
for e.Work {
p, err := os.FindProcess(e.Pid)
if err != nil {
e.Err = true
e.Block = true
return
}
err = p.Signal(syscall.Signal(0))
if err != nil {
e.Err = true
e.Block = true
return
}
// 添加休眠避免CPU空转
time.Sleep(1 * time.Second)
}
}
关键修改是添加了 time.Sleep(1 * time.Second),这会将检查间隔降低到每秒一次,显著减少CPU使用率。
另一种更高效的方法是使用 cmd.Wait() 的通道通知机制:
func (e *Exec) startProcess() error {
cmd := exec.Command(instPath, settings.ExecWorkingDir+settings.ExecFileEnvPath,
settings.ExecWorkingDir+settings.ExecFileLogPath, ":"+freeport, id)
if err := cmd.Start(); err != nil {
return err
}
e.Pid = cmd.Process.Pid
// 使用通道等待进程结束
done := make(chan error, 1)
go func() {
done <- cmd.Wait()
}()
go func() {
select {
case <-done:
e.Err = true
e.Block = true
e.stop()
}
}()
return nil
}
这种方法完全消除了轮询的需要,通过 cmd.Wait() 在子进程结束时自动触发回调,CPU使用率将保持正常水平。

