当cmd在指定的时间内完成时,以下工作正常.但是,超时不起作用.虽然它确实打印"It's dead Jim",但它不仅无法打印"Done waiting",而且该过程实际上并未被杀死.它继续运行,"Done waiting"从不打印.
我认为这是所有相关的代码,但我是Go的新手(这是我尝试过的第一个真正的项目)所以如果这还不够,请告诉我.
func() {
var output bytes.Buffer
cmd := exec.Command("Command", args...)
cmd.Dir = filepath.Dir(srcFile)
cmd.Stdout, cmd.Stderr = &output, &output
if err := cmd.Start(); err != nil {
return err
}
defer time.AfterFunc(time.Second*2, func() {
fmt.Printf("Nobody got time fo that\n")
if err := cmd.Process.Signal(syscall.SIGKILL); err != nil {
fmt.Printf("Error:%s\n", err)
}
fmt.Printf("It's dead Jim\n")
}).Stop()
err := cmd.Wait()
fmt.Printf("Done waiting\n")
}()
Run Code Online (Sandbox Code Playgroud)
我不认为它应该有所作为,但对于命令的价值而言go test html.超时的原因是因为我在运行它之前注入了导致无限循环的错误.为了增加混乱,我试着运行它go test net.有一个超时,它工作正常.
Mat*_*att 14
看起来问题是cmd.Process.Kill()不会杀死子进程.在子进程上看到类似的问题Process.Kill()
我在这个帖子中找到了一个解决方案https://groups.google.com/forum/#!topic/golang-nuts/XoQ3RhFBJl8
cmd := exec.Command( some_command )
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
cmd.Start()
pgid, err := syscall.Getpgid(cmd.Process.Pid)
if err == nil {
syscall.Kill(-pgid, 15) // note the minus sign
}
cmd.Wait()
Run Code Online (Sandbox Code Playgroud)
作为一个警告,这几乎肯定不会跨平台工作 - 我现在在OSX Yosemite,我愿意打赌它也适用于大多数Linux,但我不太了解BSD到有一个意见,我怀疑它可以在Windows上工作.
仅供参考,我也会把我的 Windows 解决方案放在这里:
func kill(cmd *exec.Cmd) error {
kill := exec.Command("TASKKILL", "/T", "/F", "/PID", strconv.Itoa(cmd.Process.Pid))
kill.Stderr = os.Stderr
kill.Stdout = os.Stdout
return kill.Run()
}
Run Code Online (Sandbox Code Playgroud)
您的调用进程可以使用setsid在posix系统上创建一个新会话。当您执行以下命令时,您的代码将成为流程组领导者(如果(目前还不是))。当你杀死进程组领导时,孩子们也会死。至少,这是我的经验。
cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
cmd.Start()
time.Sleep(5)
if err := syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL); err != nil {
log.Println("failed to kill: ", err)
}
Run Code Online (Sandbox Code Playgroud)
我不确定它是何时添加的,但是从 Go 1.11 开始,您可以将Pdeathsig子进程上的设置为syscall.SIGKILL. 这将在父母退出时杀死孩子。
cmd, _ := exec.Command("long-running command")
cmd.SysProcAttr = &syscall.SysProcAttr{
Pdeathsig: syscall.SIGKILL,
}
cmd.Start()
os.Exit(1)
Run Code Online (Sandbox Code Playgroud)
本cmd应在退出时被杀。
jef*_*uan -3
Go 的 defer 语句安排函数调用(延迟函数)在执行延迟的函数返回之前立即运行。
那么defer之后的事情
defer time.AfterFunc(time.Second*2, func() {
fmt.Printf("Nobody got time fo that\n")
cmd.Process.Kill()
fmt.Printf("It's dead Jim\n")
}).Stop()
Run Code Online (Sandbox Code Playgroud)
除非 func() 结束,否则不会执行。因此,如果“cmd.Wait()”永远不会结束,则“time.AfterFunc()”永远不会执行。
从 defer 中删除“time.AfterFunc(...)”可以解决这个问题,因为“time.AfterFunc”可以等待持续时间过去,然后在它自己的 goroutine 中调用 f 。
这是一个工作版本。我在我的 ubuntu 盒子里测试过,它可以工作。将源另存为 wait.go
package main
import "os/exec"
import "time"
import "bytes"
import "fmt"
func main() {
var output bytes.Buffer
cmd := exec.Command("sleep", "10s")
cmd.Stdout, cmd.Stderr = &output, &output
if err := cmd.Start(); err != nil {
fmt.Printf("command start error\n")
return
}
time.AfterFunc(time.Second*2, func() {
fmt.Printf("Nobody got time for that\n")
cmd.Process.Kill()
fmt.Printf("It's dead Jim\n")
})
cmd.Wait()
fmt.Printf("Done waiting\n")
}
Run Code Online (Sandbox Code Playgroud)
运行命令:
time go run wait.go
Run Code Online (Sandbox Code Playgroud)
输出:
Nobody got time for that
It's dead Jim
Done waiting
real 0m2.481s
user 0m0.252s
sys 0m0.452s
Run Code Online (Sandbox Code Playgroud)
正如 @James Henstridge 评论的那样,上述理解是不正确的。其实我对defer的理解并不完全。另一半是“延迟执行时评估延迟函数的参数(如果函数是方法,则包括接收者)”。所以当执行 defer 时,定时器才真正被创建,因此定时器会超时。
问题实际上是为什么进程不能被杀死。我检查了 go 的 pkg 代码,它在 *nix 之类的系统中发送 SIGKILL 来终止进程。SIGKILL 无法被阻止和忽略。因此可能是其他可能性,例如进程本身处于TASK_UNINTERRUPTIBLE状态。