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()调用周围设置了超时,所以我这样做是为了让它工作,不过我还是想找出正确的方法