使用同一频道的不同通话

问题描述 投票:0回答:1

您正在使用哪个版本的Go(go version?]

$ go版本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"

你做了什么?

我编写了一个函数来执行exec命令,并自行控制超时情况。而且我这样测试

package utils

import (
    "bytes"
    "context"
    "log"
    "os/exec"
    "syscall"
    "time"
)

func ExecCommand(command string, timeout time.Duration) (string, error) {
    log.Printf("command:%v, timeout:%v", command, timeout)
    var (
        cmd    *exec.Cmd
        stdout bytes.Buffer
        stderr bytes.Buffer
        result string
        err    error
        //timeouterr error
    )
    ctx, cancelFn := context.WithTimeout(context.Background(), timeout)
    defer cancelFn()

    cmd = exec.Command("bash", "-c", "--", command)
    cmd.Stdout = &stdout
    cmd.Stderr = &stderr
    cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}

    var waitDone = make(chan struct{})
    defer func() {
        log.Printf("waitDone addr:%v\n", &waitDone)
        log.Printf("close waitdone channel\n")
        close(waitDone)
    }()
    go func() {
        err = cmd.Run()
        log.Printf("waitDone addr:%v\n", &waitDone)
        waitDone <- struct{}{}
    }()

    select {
    case <-ctx.Done():
        log.Printf("timeout to kill process, %v", cmd.Process.Pid)
        syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
        result = convertStr(stdout)
        err = ctx.Err()
    case <-waitDone:
        if err != nil {
            result = convertStr(stderr)
        } else {
            result = convertStr(stdout)
        }
    }

    log.Printf("result:%v,err:%v", result, err)

    return result, err
}

func convertStr(buffer bytes.Buffer) string {
    data := buffer.String()
    return data
}
package utils

import (
    "context"
    "testing"
    "time"
)

func TestExecCommand(t *testing.T) {
    tests := []struct {
        command string
        timeout time.Duration
        wantErr string
        want    string
    }{
        {
            command: "sleep 10",
            timeout: time.Second * 5,
            wantErr: context.DeadlineExceeded.Error(),
        },
        {
            command: "watch -n 1 date +%s",
            timeout: time.Second * 10,
            wantErr: context.DeadlineExceeded.Error(),
            want:    "timeout, but still have result.",
        },
        {
            command: "hostname",
            timeout: time.Second * 5,
            wantErr: "",
            want:    "anything result would be fine.",
        },
    }

    for _, tt := range tests {
        // got panic here. 
        // send on closed channel.
        got, gotErr := ExecCommand(tt.command, tt.timeout)
        if gotErr == nil {
            if tt.wantErr == "" {
                t.Logf("succeed")
            } else {
                t.Errorf("failed case: %+v, got:%v, gotErr:%v\n", tt, got, gotErr)
            }
        } else if gotErr.Error() == tt.wantErr {
            t.Logf("succeed")
        } else {
            t.Errorf("failed case: %+v, got:%v, gotErr:%v\n", tt, got, gotErr)
        }

    }
}

您期望看到什么?

测试正常。

您看到了什么?

紧急:在封闭频道上发送。

go channel
1个回答
0
投票

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
    }()

© www.soinside.com 2019 - 2024. All rights reserved.