Search code examples
godeadlockgoroutine

waitgroup.Wait() causing dead lock


I'm trying to figure out why I have a dead lock with waitgroup.Wait()

package main

import (
    "fmt"
    "sync"
)

var wg sync.WaitGroup

func foo(c chan int, i int) {
    defer wg.Done()
    c <- i
}

func main() {
    ch := make(chan int)
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go foo(ch, i)
    }
    wg.Wait()
    close(ch)
    for item := range ch {
        fmt.Println(item)
    }
}

When I run it like this, it prints fatal error: all goroutines are asleep - deadlock!

I tried to change ch to a buffered channel and that solved the problem. But I really want to know why is there a dead lock.


Solution

  • I've commented out the parts where your program's logic is not correct:

    package main
    
    import (
        "fmt"
        "sync"
    )
    
    var wg sync.WaitGroup
    
    func foo(c chan int, i int) {
        defer wg.Done()
        c <- i
    }
    
    func main() {
        ch := make(chan int) // unbuffered channel
    
        for i := 0; i < 10; i++ {
            wg.Add(1)
            go foo(ch, i)
        }
    
        // wg.Wait is waiting for all goroutines to finish but that's
        // only possible if the send to channel succeeds. In this case,
        // it is not possible as your receiver "for item := range ch" is below
        // this. Hence, a deadlock.
        wg.Wait()
    
        // Ideally, it should be the sender's duty to close the channel.
        // And closing a channel before the receiver where the channel
        // is unbuffered is not correct.
        close(ch)
    
        for item := range ch {
            fmt.Println(item)
        }
    }
    

    Corrected program:

    package main
    
    import (
        "fmt"
        "sync"
    )
    
    var wg sync.WaitGroup
    
    func foo(c chan int, i int) {
        defer wg.Done()
        c <- i
    }
    
    func main() {
        ch := make(chan int)
    
        go func() {
            for item := range ch {
                fmt.Println(item)
            }
        }()
    
        for i := 0; i < 10; i++ {
            wg.Add(1)
            go foo(ch, i)
        }
    
        wg.Wait()
        close(ch)
    }