为了账号安全,请及时绑定邮箱和手机立即绑定

使用相同通道的不同呼叫

使用相同通道的不同呼叫

Go
HUH函数 2023-07-31 15:47:38
您使用的 Go 版本是什么(go version)?$ go version 1.13.1最新版本是否会重现此问题?我不知道。您使用什么操作系统和处理器架构(go env)?$ go envGO111MODULE="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"你做了什么?
查看完整描述

2 回答

?
跃然一笑

TA贡献1826条经验 获得超6个赞

当你的函数退出时,你正在关闭通道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)

这可以使您免于使用这种复杂的逻辑来检查超时。


查看完整回答
反对 回复 2023-07-31
?
侃侃尔雅

TA贡献1801条经验 获得超15个赞

考虑使用exec.CommandContext而不是自己编写此代码。

在命令完成之前上下文超时的情况下,该ExecCommand函数可以在 Run goroutine 发送到通道之前关闭通道。这会引起恐慌。

waitDone由于应用程序在执行后不会收到任何消息close(waitDone),因此关闭通道是没有意义的。

如果删除关闭通道的代码,就会暴露另一个问题。因为是一个无缓冲的通道,所以在超时情况下,waitDoneRun goroutine 将在发送时永远阻塞。waitDone

调用cmd.Run()启动一个 goroutine 将数据复制到stdoutstderr。无法保证这些 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()

    }

}


查看完整回答
反对 回复 2023-07-31
  • 2 回答
  • 0 关注
  • 110 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信