我需要运行一个长时间运行的子进程,并且如果我(出于任何原因)退出父应用程序而将其杀死。
这是代码:
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
它按预期方式工作-子进程被终止。
但这在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
没有被调用,子进程也没有被杀死。
有什么办法解决吗?
更新 我需要一个跨平台的解决方案(至少对于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。您还可以让某种流程管理器监视流程并在出现问题时进行清除。
通常,恐慌应该很少见并得到解决。如果您确实有容易发生恐慌的代码区域,则可以在退出之前在本地恢复并要求清除子进程。
问题内容: 我曾经认为,如果goroutine中的恐慌的调用者在恐慌之前完成,它将使其终止程序(延迟恢复没有任何帮助,因为此时还没有发生恐慌), 直到我尝试以下代码: 我发现无论调用者函数完成与否,如果goroutines开始恐慌,调用者的延迟恢复机制将无济于事。整个程序仍然无效。 所以为什么?理论上,调用者功能仍在运行。当出现紧急情况时,调用者的延迟功能应起作用(包括恢复)。 问题答案: 该规范
问题内容: 我有一个goroutine,它调用一个方法,并在通道上传递返回的值: 如何停止这种goroutine? 问题答案: 编辑: 在意识到您的问题是关于将值发送到goroutine中的chan之前,我匆忙编写了此答案。 下面的方法可以与上面建议的其他chan一起使用,或者利用您已经拥有的chan双向的事实,您可以只使用一个… 如果您的goroutine仅用于处理来自chan的项目,则可以使用
我曾经认为goroutine中的恐慌会杀死程序,如果它的调用者在恐慌之前完成(延迟恢复没有任何帮助,因为在这一点上还没有恐慌发生), 直到我尝试下面的代码: 我发现无论调用方函数是否完成,如果goroutines开始恐慌,调用方的延迟恢复机制都没有帮助。整个程序还是死气沉沉的。
问题内容: 调用反射值的.FieldByName方法时出现以下错误,确切的错误是:- 和代码是:- 我了解的并不多,但这就是我所能获得的所有信息。 这是Go Playground上代码的链接:http : //play.golang.org/p/E038cPOoGp 问题答案: 您已经是一个指向结构的指针。尝试打印出您的代码。 没有理由使用的地址,然后调用that ,它会取消对刚创建的指针的引用。
我目前正在考虑如何编写测试来检查给定的代码是否出现了恐慌?我知道Go使用来捕捉恐慌,但与Java代码不同的是,您不能真正指定在发生恐慌时应该跳过哪些代码或您有哪些代码。所以如果我有一个函数: 我真的不知道是否发生了恐慌,我们恢复了,或者函数是否根本没有恐慌。如何指定在没有恐慌的情况下跳过哪些代码,以及在出现恐慌的情况下执行哪些代码?我如何检查我们是否从恐慌中恢复过来?
在Golang中,没有恢复的panic将使进程崩溃,因此我最后将以下代码片段放在每个函数的开头: 只是为了防止我的程序崩溃。现在我在想,真的是要走的路吗?因为我觉得到处放同样的代码看起来有点奇怪。 在我看来,这是Java的方式,将异常冒泡到调用函数,直到main函数成为控制异常/恐慌的更好方式。我明白这是Go的设计,但像Go一样立即崩溃进程的好处是什么?