致命错误:所有 goroutine 都在睡觉 - 死锁!将 WaitGroup

go版本: go1.12.5 linux/amd64


我试图理解 Go 中的 nil 通道。


package main


import (

    "fmt"

    "sync"

)


func main() {

    ch := make(chan int)

    ch2 := make(chan int)

    wg := sync.WaitGroup{}

    wg.Add(1)

    go func(c1 chan int, c2 chan int, w *sync.WaitGroup) {

        for c1 != nil || c2 != nil {

            fmt.Println("in for")

            fmt.Println(c1, c2)

            select {

            case v, ok := <-c1:

                if !ok {

                    c1 = nil

                    fmt.Println("c1 closed")

                } else {

                    fmt.Println(v, " recieved c1")

                }

            case v, ok := <-c2:

                if !ok {

                    c2 = nil

                } else {

                    fmt.Println(v, " recieved c2")

                }

            }

        }

        fmt.Println("called wg.Done")

        wg.Done()

    }(ch, ch2, &wg)

    for i := 0; i < 4; i++ {

        if i%2 == 0 {

            ch <- i

        } else {

            ch2 <- i

        }

    }

    close(ch)

    wg.Wait()

}


并在标准输出上收到此输出:


in for

0xc000084060 0xc0000840c0

0  recieved c1

in for

0xc000084060 0xc0000840c0

1  recieved c2

in for

0xc000084060 0xc0000840c0

2  recieved c1

in for

0xc000084060 0xc0000840c0

3  recieved c2

in for

0xc000084060 0xc0000840c0

c1 closed

in for

<nil> 0xc0000840c0

fatal error: all goroutines are asleep - deadlock!


goroutine 1 [semacquire]:

sync.runtime_Semacquire(0xc00009a018)

        /usr/local/go/src/runtime/sema.go:56 +0x39

sync.(*WaitGroup).Wait(0xc00009a010)

        /usr/local/go/src/sync/waitgroup.go:130 +0x65

main.main()

        /home/ayush/projects/gojects/src/go-practice/nil-channels/main.go:44 +0x155


goroutine 18 [select]:

main.main.func1(0xc00009a010, 0xc000084060, 0xc0000840c0, 0xc00009a010)

        /home/ayush/projects/gojects/src/go-practice/nil-channels/main.go:17 +0x1e5

created by main.main

        /home/ayush/projects/gojects/src/go-practice/nil-channels/main.go:13 +0xce

exit status 2

但根据代码和日志,wg.Done()从未被调用过,这意味着 goroutine 仍然存在。


谁能帮我理解这里发生了什么?


函数式编程
浏览 98回答 2
2回答

慕沐林林

你只有靠近ch,没有ch2。当ch关闭时,goroutine 设置c1为 nil,但c2仍然不是 nil,因此select等待从 接收c2,而主 goroutine 等待wg.Done(),因此两个 goroutine 都处于睡眠状态。

人到中年有点甜

这是因为你没有关闭而发生的ch2。选择块仍在等待ch2。工作代码:package mainimport (&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "sync")func main() {&nbsp; &nbsp; ch := make(chan int)&nbsp; &nbsp; ch2 := make(chan int)&nbsp; &nbsp; wg := sync.WaitGroup{}&nbsp; &nbsp; wg.Add(1)&nbsp; &nbsp; go func(c1 chan int, c2 chan int, w *sync.WaitGroup) {&nbsp; &nbsp; &nbsp; &nbsp; for c1 != nil || c2 != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; select {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case v, ok := <-c1:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if !ok {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; c1 = nil&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("c1 closed")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(v, " recieved c1")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case v, ok := <-c2:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if !ok {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; c2 = nil&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(v, " recieved c2")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("called wg.Done")&nbsp; &nbsp; &nbsp; &nbsp; wg.Done()&nbsp; &nbsp; }(ch, ch2, &wg)&nbsp; &nbsp; for i := 0; i < 4; i++ {&nbsp; &nbsp; &nbsp; &nbsp; if i%2 == 0 {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ch <- i&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("sending to c1 ", i)&nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ch2 <- i&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("sending to c2 ", i)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; close(ch)&nbsp; &nbsp; close(ch2)&nbsp; &nbsp; wg.Wait()}
打开App,查看更多内容
随时随地看视频慕课网APP