使用相同通道的不同呼叫

您使用的 Go 版本是什么(go version)?

$ go version 1.13.1

最新版本是否会重现此问题?

我不知道。

您使用什么操作系统和处理器架构(go env)?

$ go env

GO111MODULE="auto"

GOARCH="amd64"

GOBIN="/usr/local/go/bin"

GOCACHE="/data/xieyixin/.cache/go-build"

GOENV="/data/xieyixin/.config/go/env"

GOEXE=""

GOFLAGS=""

GOHOSTARCH="amd64"

GOHOSTOS="linux"

GONOPROXY=""

GONOSUMDB=""

GOOS="linux"

GOPATH="/data/xieyixin/go"

GOPRIVATE=""

GOPROXY="http://10.0.12.201:8989/"

GOROOT="/usr/local/go"

GOSUMDB="sum.golang.org"

GOTMPDIR=""

GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"

GCCGO="gccgo"

AR="ar"

CC="gcc"

CXX="g++"

CGO_ENABLED="1"

GOMOD="/data/xieyixin/hxagent/go.mod"

CGO_CFLAGS="-g -O2"

CGO_CPPFLAGS=""

CGO_CXXFLAGS="-g -O2"

CGO_FFLAGS="-g -O2"

CGO_LDFLAGS="-g -O2"

PKG_CONFIG="pkg-config"

GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build474907248=/tmp/go-build"

你做了什么?


HUH函数
浏览 118回答 2
2回答

跃然一笑

当你的函数退出时,你正在关闭通道ExecCommand。由于您在 Goroutine 中发送消息,因此无法保证该消息会在函数退出之前发送。事实上,我每次跑步都是在之后发生的。如果没有第一个可推迟的内容,您的测试将正常工作。    defer func() {        log.Printf("waitDone addr:%v\n", &waitDone)        log.Printf("close waitdone channel\n")        close(waitDone) // <- here     }()    go func() {        err = cmd.Run()        log.Printf("waitDone addr:%v\n", &waitDone)        waitDone <- struct{}{}  // <- and here    }()由于您已经在使用超时上下文,因此这将非常适合cmd = exec.CommandContext(ctx, "bash", "-c", "--", command)// cmd = exec.Command("bash", "-c", "--", command)这可以使您免于使用这种复杂的逻辑来检查超时。

侃侃尔雅

考虑使用exec.CommandContext而不是自己编写此代码。在命令完成之前上下文超时的情况下,该ExecCommand函数可以在 Run goroutine 发送到通道之前关闭通道。这会引起恐慌。waitDone由于应用程序在执行后不会收到任何消息close(waitDone),因此关闭通道是没有意义的。如果删除关闭通道的代码,就会暴露另一个问题。因为是一个无缓冲的通道,所以在超时情况下,waitDoneRun goroutine 将在发送时永远阻塞。waitDone调用cmd.Run()启动一个 goroutine 将数据复制到stdout和stderr。无法保证这些 goroutine 在ExecCommand调用convertStr(stdout)或之前执行完毕convertStr(stderr)。这是解决所有这些问题的一个方法:func ExecCommand(command string, timeout time.Duration) (string, error) {    log.Printf("command:%v, timeout:%v", command, timeout)    ctx, cancelFn := context.WithTimeout(context.Background(), timeout)    defer cancelFn()    var stdout, stderr bytes.Buffer    cmd := exec.Command("bash", "-c", "--", command)    cmd.Stdout = &stdout    cmd.Stderr = &stderr    cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}    err := cmd.Start()    if err != nil {        return "", err    }    go func() {        <-ctx.Done()        if ctx.Err() == context.DeadlineExceeded {            log.Printf("timeout to kill process, %v", cmd.Process.Pid)            syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)        }    }()    err = cmd.Wait()    var result string    if err != nil {        result = stderr.String()    } else {        result = stdout.String()    }}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go