dsc7188 2017-01-06 14:28
浏览 64
已采纳

Golang中的可选超时

I have a function which runs a command with a timeout. It looks like this:

func run_command(cmdName string, cmdArgs []string, timeout int) (int, string) {

  // the command we're going to run
  cmd := exec.Command(cmdName, cmdArgs...)

  // assign vars for output and stderr
  var output bytes.Buffer
  var stderr bytes.Buffer

  // get the stdout and stderr and assign to pointers
  cmd.Stderr = &stderr
  cmd.Stdout = &output

  // Start the command
  if err := cmd.Start(); err != nil {
    log.Fatalf("Command not found: %s", cmdName)
  }

  timer := time.AfterFunc(time.Second*time.Duration(timeout), func() {
    err := cmd.Process.Kill()
    if err != nil {
      panic(err)
    }
  })

  // Here's the good stuff
  if err := cmd.Wait(); err != nil {
    if exiterr, ok := err.(*exec.ExitError); ok {
      // Command ! exit 0, capture it
      if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
        // Check it's nagios compliant
        if status.ExitStatus() == 1 || status.ExitStatus() == 2 || status.ExitStatus() == 3 {
          return status.ExitStatus(), stderr.String()
        } else {
          // If not, force an exit code 2
          return 2, stderr.String()
        }
      }
    } else {
      log.Fatalf("cmd.Wait: %v", err)
    }
    timer.Stop()
  }
  // We didn't get captured, continue!
  return 0, output.String()
}

Now I want to be able to make the timeout optional. In order to fudge this a bit, I tried simply allowing timeout to be set to 0 and then having an if statement around the timer. It ended up looking like this.

if timeout > 0 {
  timer := time.AfterFunc(time.Second*time.Duration(timeout), func() {
    err := cmd.Process.Kill()
    if err != nil {
      panic(err)
    }
  })
}

Of course, this failed because timer is no longer defined timer.Stop() isn't defined now.

So I wrapped the timer.Stop() with the if statement as well.

if timeout > 0 {
  timer.Stop()
}

This also didn't work.

What is the correct way to do something like this? Golangs strict typing is new to me, so I'm struggling to get my head around it

  • 写回答

3条回答 默认 最新

  • douzi8916 2017-01-06 15:32
    关注

    Using the context package makes it easy to handle timeouts. golang.org/x/net/context has become a standard library since Go 1.7. The following is an example:

    package main
    
    import (
        "context"
        "os"
        "os/exec"
        "strconv"
        "time"
    )
    
    func main() {
        timeout, err := strconv.Atoi(os.Args[1])
        if err != nil {
            panic(err)
        }
    
        ctx := context.Background()
        if timeout > 0 {
            var cancel context.CancelFunc
            ctx, cancel = context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
            defer cancel()
        }
    
        cmd := exec.CommandContext(ctx, "sleep", "5")
    
        if err := cmd.Run(); err != nil {
            panic(err)
        }
    }
    

    When timeout is set to 3 seconds, and run sleep 5:

    $ go run main.go 3
    panic: signal: killed
    
    goroutine 1 [running]:
    panic(0xc7040, 0xc42008c020)
            /usr/local/Cellar/go/1.7.4_1/libexec/src/runtime/panic.go:500 +0x1a1
    main.main()
            /Users/m-morita/work/tmp/20170106/main.go:27 +0x11c
    exit status 2
    

    When it is set to 10 seconds or 0(= never timeout), it ends normally:

    $ go run main.go 10 
    $ go run main.go 0
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(2条)

报告相同问题?

悬赏问题

  • ¥15 如何在scanpy上做差异基因和通路富集?
  • ¥20 关于#硬件工程#的问题,请各位专家解答!
  • ¥15 关于#matlab#的问题:期望的系统闭环传递函数为G(s)=wn^2/s^2+2¢wn+wn^2阻尼系数¢=0.707,使系统具有较小的超调量
  • ¥15 FLUENT如何实现在堆积颗粒的上表面加载高斯热源
  • ¥30 截图中的mathematics程序转换成matlab
  • ¥15 动力学代码报错,维度不匹配
  • ¥15 Power query添加列问题
  • ¥50 Kubernetes&Fission&Eleasticsearch
  • ¥15 報錯:Person is not mapped,如何解決?
  • ¥15 c++头文件不能识别CDialog