douqiao5543 2015-12-04 18:39
浏览 65
已采纳

其他goroutine中的恐慌无法停止子进程

I need to run a long-running child process and kill it if I quit (for any reason) out of parent application.

Here is the code:

cmd := exec.Command("./long-process")

defer cmd.Process.Kill()

if err != nil {
    log.Fatal(err)
}

var fail io.ReadCloser
fail.Close()

The fail here produces obvious

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

It works as expected - the child process is killed.

But this happens in a goroutine:

cmd := exec.Command("./long-process")

defer cmd.Process.Kill()

if err != nil {
    log.Fatal(err)
}

go func() {
    var fail io.ReadCloser
    fail.Close()
}()

The panic still happens, but then it seems defer is not called and the child process is not killed.

Any way to go around this?

UPDATE I need a cross-platform solution (at least for Linux and FreeBSD)

Minimal example:

infinite-loop.sh

#!/bin/bash

while true; do
  sleep 1
done

Don't forget to

chmod +x infinite-loop.sh

test1.go (error checking left out for brevity):

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()
    }
}

Let's run

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 processes before and 1 after exit.

If you comment out goroutine code - it works fine.

Now we can kill it:

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

1条回答 默认 最新

  • dongzang5815 2015-12-04 19:18
    关注

    There's no cross-platform solution to automatically kill a child process.

    On Linux, you can use the pdeathsig functionality:

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

    On other platforms, the child needs to determine when to exit on its own. One way is to monitor a pipe or socket FD given to it from the parent. You could also have a process manager of some sort monitor the processes and cleanup if something goes wrong.

    In general though, panics should be rare and get fixed. If you do have areas of code that are prone to panic'ing, you can recover locally and call for the cleanup of child processes before exiting.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥20 csv格式数据集预处理及模型选择
  • ¥15 部分网页页面无法显示!
  • ¥15 怎样解决power bi 中设置管理聚合,详细信息表和详细信息列显示灰色,而不能选择相应的内容呢?
  • ¥15 QTOF MSE数据分析
  • ¥15 平板录音机录音问题解决
  • ¥15 请问维特智能的安卓APP在手机上存储传感器数据后,如何找到它的存储路径?
  • ¥15 (SQL语句|查询结果翻了4倍)
  • ¥15 Odoo17操作下面代码的模块时出现没有'读取'来访问
  • ¥50 .net core 并发调用接口问题
  • ¥15 网上各种方法试过了,pip还是无法使用