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 2020长安杯与连接网探
  • ¥15 关于#matlab#的问题:在模糊控制器中选出线路信息,在simulink中根据线路信息生成速度时间目标曲线(初速度为20m/s,15秒后减为0的速度时间图像)我想问线路信息是什么
  • ¥15 banner广告展示设置多少时间不怎么会消耗用户价值
  • ¥16 mybatis的代理对象无法通过@Autowired装填
  • ¥15 可见光定位matlab仿真
  • ¥15 arduino 四自由度机械臂
  • ¥15 wordpress 产品图片 GIF 没法显示
  • ¥15 求三国群英传pl国战时间的修改方法
  • ¥15 matlab代码代写,需写出详细代码,代价私
  • ¥15 ROS系统搭建请教(跨境电商用途)