Go 戈朗可选超时

Go 戈朗可选超时,go,timer,Go,Timer,我有一个函数,它运行一个带有超时的命令。看起来是这样的: 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.Bu

我有一个函数,它运行一个带有超时的命令。看起来是这样的:

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()
}
现在我想让超时成为可选的。为了有点含糊其辞,我尝试简单地将timeout设置为
0
,然后在计时器周围使用if语句。结果是这样的

if timeout > 0 {
  timer := time.AfterFunc(time.Second*time.Duration(timeout), func() {
    err := cmd.Process.Kill()
    if err != nil {
      panic(err)
    }
  })
}
当然,这失败了,因为计时器不再定义
timer.Stop()
现在没有定义

因此,我用if语句包装了
timer.Stop()

if timeout > 0 {
  timer.Stop()
}
这也没用


这样做的正确方法是什么?Golangs严格的键入对我来说是新的,所以我正努力想办法解决它

如果没有持续时间,你可以用noop替换timer func,通常的解决方案是简单地延迟
计时器。创建计时器时停止调用

timer := time.AfterFunc(time.Second*time.Duration(timeout), func() {
    err := cmd.Process.Kill()
    if err != nil {
        panic(err)
    }
})
defer timer.Stop()
否则,您可以在函数作用域中声明
timer
,并在调用
timer.Stop()


您还应该注意,
exec.Cmd
已经使用了for timeout,这是通过。

公开的。如果没有持续时间,您可以用noop替换timer func,通常的解决方案是简单地延迟
timer。创建计时器时停止
调用:

timer := time.AfterFunc(time.Second*time.Duration(timeout), func() {
    err := cmd.Process.Kill()
    if err != nil {
        panic(err)
    }
})
defer timer.Stop()
否则,您可以在函数作用域中声明
timer
,并在调用
timer.Stop()


您还应该注意,
exec.Cmd
已经使用了for timeout,它是通过公开的。

只需在第一个if timeout>0块之前定义计时器变量,并使用=而不是:=将计时器分配给它

var timer *time.Timer
if timeout > 0 {
  timer = time.AfterFunc(time.Second*time.Duration(timeout), func() {
    err := cmd.Process.Kill()
    if err != nil {
      panic(err)
    }
  })
}
仍然需要在timer.Stop()之前检查timeout>0,或者,为了减少依赖关系,更改为timer!=零

if timer != nil {
  timer.Stop()
}

只需在第一个if timeout>0块之前定义计时器变量,并使用=而不是:=将计时器分配给它

var timer *time.Timer
if timeout > 0 {
  timer = time.AfterFunc(time.Second*time.Duration(timeout), func() {
    err := cmd.Process.Kill()
    if err != nil {
      panic(err)
    }
  })
}
仍然需要在timer.Stop()之前检查timeout>0,或者,为了减少依赖关系,更改为timer!=零

if timer != nil {
  timer.Stop()
}

使用
上下文
包可以轻松处理超时。 自Go 1.7以来,
golang.org/x/net/context
已成为标准库。 以下是一个例子:

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)
    }
}
当超时设置为3秒时,运行
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
当设置为10秒或0(=从不超时)时,正常结束:

$ go run main.go 10 
$ go run main.go 0

使用
上下文
包可以轻松处理超时。 自Go 1.7以来,
golang.org/x/net/context
已成为标准库。 以下是一个例子:

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)
    }
}
当超时设置为3秒时,运行
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
当设置为10秒或0(=从不超时)时,正常结束:

$ go run main.go 10 
$ go run main.go 0

在这之后我意识到的一件事是,我在
cmd.Process.Kill()
调用周围设置了超时,所以我这样做是为了让它工作,但是我仍然想找出正确的方法,在这之后我意识到的一件事是,我在
cmd.Process.Kill()调用周围设置了超时,所以我这样做是为了让它工作,不过我还是想找出正确的方法