duanpo1821 2019-07-28 09:42
浏览 31
已采纳

为什么这种goroutine泄漏?

I am reading 'Concurrency in Go' and have found this example of goroutine leaks:

func main() {

    var wg sync.WaitGroup

    doWork := func(strings <-chan string) <-chan interface{} {
        completed := make(chan interface{})
        go func() {
            defer fmt.Println("doWork exited.")
            defer close(completed)
            defer wg.Done()
            fmt.Println("a")
            for s := range strings {
                fmt.Println(s)
            }
            fmt.Println("b")
        }()
        return completed
    }

    wg.Add(1)
    doWork(nil)
    fmt.Println("Waiting")
    wg.Wait()

    fmt.Println("Done.")
}

The strings channel will never gets any strings written onto it, and the goroutine containing doWork will remain in memory for the life time of process.

I don't understand - why ?

How I understand this code:

  • As strings is nil range-loop just skipped. As any range over nil:

    slice := []int{10, 20, 30, 40, 50}
    slice = nil
    for i := range slice {
       fmt.Println(i)
    }
    fmt.Println("Done")
    
  • fmt.Println("doWork exited.") will be executed

  • close(completed) will be executed

But I see it works like that. Why ?

  • 写回答

1条回答 默认 最新

  • duancongjue9202 2019-07-28 10:04
    关注

    As strings is nil range-loop just skipped.

    This assumption is not correct. In Go, reading from a nil channel will always block. This is defined in the language specification (thanks to @peterSO for digging out the link):

    Receiving from a nil channel blocks forever.

    There's also a post on the Go Design Patterns blog that further elaborates on this behaviour and highlights some cases in which it is useful.

    Anyhow, this behaviour can easily be reproduced with a minimal example (playground):

    func main() {
        var s chan string
        <- s
    }
    

    This program will never finish (in the playground, it will crash with all goroutines are asleep - deadlock).

    Because reading from a nil channel (in the case of your example, strings) will block (forever, since nothing can be written into a nil channel), the doWork goroutine will never complete, and thus, leak.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?