2 回答

TA貢獻(xiàn)1826條經(jīng)驗(yàn) 獲得超6個(gè)贊
當(dāng)你的函數(shù)退出時(shí),你正在關(guān)閉通道ExecCommand。由于您在 Goroutine 中發(fā)送消息,因此無法保證該消息會(huì)在函數(shù)退出之前發(fā)送。事實(shí)上,我每次跑步都是在之后發(fā)生的。如果沒有第一個(gè)可推遲的內(nèi)容,您的測(cè)試將正常工作。
? ? 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
? ? }()
由于您已經(jīng)在使用超時(shí)上下文,因此這將非常適合
cmd = exec.CommandContext(ctx, "bash", "-c", "--", command)
// cmd = exec.Command("bash", "-c", "--", command)
這可以使您免于使用這種復(fù)雜的邏輯來檢查超時(shí)。

TA貢獻(xiàn)1801條經(jīng)驗(yàn) 獲得超16個(gè)贊
考慮使用exec.CommandContext而不是自己編寫此代碼。
在命令完成之前上下文超時(shí)的情況下,該ExecCommand
函數(shù)可以在 Run goroutine 發(fā)送到通道之前關(guān)閉通道。這會(huì)引起恐慌。
waitDone
由于應(yīng)用程序在執(zhí)行后不會(huì)收到任何消息close(waitDone)
,因此關(guān)閉通道是沒有意義的。
如果刪除關(guān)閉通道的代碼,就會(huì)暴露另一個(gè)問題。因?yàn)槭且粋€(gè)無緩沖的通道,所以在超時(shí)情況下,waitDone
Run goroutine 將在發(fā)送時(shí)永遠(yuǎn)阻塞。waitDone
調(diào)用cmd.Run()
啟動(dòng)一個(gè) goroutine 將數(shù)據(jù)復(fù)制到stdout
和stderr
。無法保證這些 goroutine 在ExecCommand
調(diào)用convertStr(stdout)
或之前執(zhí)行完畢convertStr(stderr)
。
這是解決所有這些問題的一個(gè)方法:
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()
? ? }
}
- 2 回答
- 0 關(guān)注
- 162 瀏覽
添加回答
舉報(bào)