我需要运行一个长时间运行的子进程,并且如果我(出于任何原因)退出父应用程序而将其杀死。
这是代码:
cmd := exec.Command("./long-process") defer cmd.Process.Kill() if err != nil { log.Fatal(err) } var fail io.ReadCloser fail.Close()
在fail这里产生明显
fail
panic: runtime error: invalid memory address or nil pointer dereference
它按预期方式工作-子进程被终止。
但这在goroutine中发生:
cmd := exec.Command("./long-process") defer cmd.Process.Kill() if err != nil { log.Fatal(err) } go func() { var fail io.ReadCloser fail.Close() }()
恐慌仍然发生,但是随后似乎defer没有被调用,子进程也没有被杀死。
defer
有什么办法解决吗?
更新 我需要一个跨平台的解决方案(至少对于Linux和FreeBSD)
最小示例:
infinite-loop.sh
#!/bin/bash while true; do sleep 1 done
别忘了
chmod +x infinite-loop.sh
test1.go (为简便起见,未进行错误检查):
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功能:
pdeathsig
cmd := exec.Command("./long-process") cmd.SysProcAttr = &syscall.SysProcAttr{ Pdeathsig: syscall.SIGTERM, }
在其他平台上,孩子需要确定何时自行退出。一种方法是监视从父级分配给它的管道或插座FD。您还可以让某种流程管理器监视流程并在出现问题时进行清除。
通常,恐慌应该很少见并得到解决。如果您确实有容易发生恐慌的代码区域,则可以在退出之前在本地恢复并要求清除子进程。