恐慌在其他goroutine没有停止子进程

  • 本文关键字:子进程 goroutine 其他 go
  • 更新时间 :
  • 英文 :


我需要运行一个长时间运行的子进程,如果我(出于任何原因)退出父应用程序,则需要终止它。

这是代码:

cmd := exec.Command("./long-process")
defer cmd.Process.Kill()
if err != nil {
    log.Fatal(err)
}
var fail io.ReadCloser
fail.Close()

这里的fail产生明显的

panic: runtime error: invalid memory address or nil pointer dereference

它按预期工作-子进程被终止。

但这发生在一场闹剧中:

cmd := exec.Command("./long-process")
defer cmd.Process.Kill()
if err != nil {
    log.Fatal(err)
}
go func() {
    var fail io.ReadCloser
    fail.Close()
}()

恐慌仍然会发生,但似乎defer没有被调用,子进程也没有被终止。

有什么办法绕过这个吗?

UPDATE我需要一个跨平台的解决方案(至少适用于Linux和FreeBSD)

最小示例:

infinite-loop.sh

#!/bin/bash
while true; do
  sleep 1
done

别忘了

chmod +x infinite-loop.sh

test1.go(为简洁起见,省略了错误检查):

package main
import (
    "time"
    "io"
    "os/exec"
    "runtime"
)
func main() {
    cmd := exec.Command("./infinite-loop.sh")
    cmd.Start()
    defer cmd.Process.Kill()
    go func() {
        time.Sleep(100 * time.Millisecond)
        var fail io.ReadCloser
        fail.Close()
    }()
    for {
        runtime.Gosched()
    }
}

让我们运行

ps aux | grep infinite-loop.sh | grep -v grep | wc -l; 
go run test1.go; 
ps aux | grep infinite-loop.sh | grep -v grep | wc -l

     0 <--- !!

panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x20 pc=0x2130]
goroutine 5 [running]:
main.main.func1()
.../multiline/test1.go:19 +0x30
created by main.main
.../multiline/test1.go:20 +0x9a
goroutine 1 [runnable]:
runtime.Gosched()
/usr/local/Cellar/go/1.5.1/libexec/src/runtime/proc.go:166 +0x14
main.main()
.../multiline/test1.go:23 +0x9f
exit status 2
     1 <--- !!

退出前有0个进程,退出后有1个进程。

如果您注释掉goroutine代码,它会很好地工作。

现在我们可以杀死它:

kill $(ps aux | grep infinite-loop.sh | grep -v grep | awk {'print $2'})

没有跨平台的解决方案可以自动终止子进程。

在Linux上,您可以使用pdeathsig功能:

cmd := exec.Command("./long-process")
cmd.SysProcAttr = &syscall.SysProcAttr{
    Pdeathsig: syscall.SIGTERM,
}

在其他平台上,孩子需要自己决定何时退出。一种方法是监视从父对象提供给它的管道或套接字FD。您还可以有某种类型的流程管理器来监控流程,并在出现问题时进行清理。

不过,总的来说,恐慌应该是罕见的,并得到解决。如果你确实有容易恐慌的代码区域,你可以在本地恢复,并在退出之前调用清理子进程。

最新更新