为什么我的go程序会出现死锁?

时间:2016-05-11 08:25:24

标签: go

我是golang编程的新手,我有以下程序生成deadlock,我不明白为什么?

另一件事是如果我用doAdd方法关闭通道然后我进入一个无限循环,这对我来说也有点奇怪。

这是该计划。

var wg sync.WaitGroup

func main() {

    ch1 := make(chan string)
    ch2 := make(chan string)
    ch3 := make(chan string)
    chClose := make(chan bool)
    wg.Add(3)
    go doAdd(ch1, "ch1")
    go doAdd(ch2, "ch2")
    go doAdd(ch3, "ch3")

    go waitForClose(chClose)

    for {
        select {
        case x := <-ch1:
            fmt.Println("Got from ch1 ", x)
        case y := <-ch2:
            fmt.Println("Got from ch2 ", y)
        case z := <-ch3:
            fmt.Println("Got from ch3 ", z)
        case <-chClose:
            fmt.Println("CLOSED")
            break
        }
    }
}

func waitForClose(chClose chan bool) {
    wg.Wait()
    chClose <- true
}

func doAdd(ch chan string, name string) {
    for i := 0; i < 10; i++ {
        ch <- strconv.Itoa(i)
    }
    wg.Done()
}

,输出为:

Got from ch1  0
Got from ch1  1
Got from ch1  2
Got from ch1  3
Got from ch1  4
Got from ch1  5
Got from ch1  6
Got from ch1  7
Got from ch1  8
Got from ch1  9
Got from ch2  0
Got from ch2  1
Got from ch2  2
Got from ch2  3
Got from ch2  4
Got from ch2  5
Got from ch2  6
Got from ch2  7
Got from ch2  8
Got from ch2  9
Got from ch3  0
Got from ch3  1
Got from ch3  2
Got from ch3  3
Got from ch3  4
Got from ch3  5
Got from ch3  6
Got from ch3  7
Got from ch3  8
Got from ch3  9
CLOSED
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [select]:
main.main()
        c:/PraveenData/demo/go-work/main.go:29 +0x915
exit status 2

2 个答案:

答案 0 :(得分:2)

您遇到死锁的原因是break内的select只会突破select,让for循环可以重新进入选择状态,没有任何通道准备好读取。

您可以通过执行以下操作来解救此问题:

done := false

for !done {
        select {
               ...
        case <-chClose:
                done = true
                fmt.Println("CLOSED")
        }
}

这通常允许for循环终止。

另一种方法是使用标签:

OuterLoop:
        for {
                select {
                ...
                case <-chClose:
                        fmt.Println("CLOSED")
                        break OuterLoop
                }
        }

我个人对这种情况下的第一个版本略有偏好,但这只是一个品味问题。

答案 1 :(得分:1)

您计划结束时的break只会突破select(并再次进入循环,因此死锁):将其替换为return正常工作:{ {3}}

确实,来自https://play.golang.org/p/j5bDaj3z7y

  

“break”语句终止在同一函数中执行最里面的“for”,“switch”或“select”语句。

你可以使用return(正如我所做的),goto或其他一些架构重构来解决这个问题。

至于无限循环,这是同样的问题,而是封闭的频道总是返回,所以当break退出select时,你回到循环,并接收{{1}来自封闭渠道的永远