dhamechaSpeaks
dhamechaSpeaks

Reputation: 137

Deadlocks with buffered channels in Go

I am encountering for the below code fatal error: all goroutines are asleep - deadlock!

Am I right in using a buffered channel? I would appreciate it if you can give me pointers. I am unfortunately at the end of my wits.

func main() {
    valueChannel := make(chan int, 2)
    defer close(valueChannel)
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go doNothing(&wg, valueChannel)
    }

    for {
        v, ok := <- valueChannel
        if !ok {
            break
        }
        fmt.Println(v)
    }
    wg.Wait()
}

func doNothing(wg *sync.WaitGroup, numChan chan int) {
    defer wg.Done()
    time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
    numChan <- 12
}

Upvotes: 1

Views: 134

Answers (2)

Thundercat
Thundercat

Reputation: 120979

The main goroutine blocks on <- valueChannel after receiving all values. Close the channel to unblock the main goroutine.

func main() {
    valueChannel := make(chan int, 2)
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go doNothing(&wg, valueChannel)
    }

    // Close channel after goroutines complete.
    go func() {
        wg.Wait()
        close(valueChannel)
    }()

    // Receive values until channel is closed. 
    // The for / range loop here does the same
    // thing as the for loop in the question.
    for v := range valueChannel {
        fmt.Println(v)
    }
 }

Run the example on the playground.

The code above works independent of the number of values sent by the goroutines. If the main() function can determine the number of values sent by the goroutines, then receive that number of values from main():

func main() {
    const n = 10

    valueChannel := make(chan int, 2)
    for i := 0; i < n; i++ {
        go doNothing(valueChannel)
    }

    // Each call to doNothing sends one value. Receive
    // one value for each call to doNothing.
    for i := 0; i < n; i++ {
        fmt.Println(<-valueChannel)
    }
}

func doNothing(numChan chan int) {
    time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
    numChan <- 12
}

Run the example on the playground.

Upvotes: 2

Cornos
Cornos

Reputation: 283

The main problem is on the for loop of channel receiving. The comma ok idiom is slightly different on channels, ok indicates whether the received value was sent on the channel (true) or is a zero value returned because the channel is closed and empty (false). In this case the channel is waiting a data to be sent and since it's already finished sending the value ten times : Deadlock. So apart of the design of the code if I just need to do the less change possible here it is:

func main() {
    valueChannel := make(chan int, 2)
    defer close(valueChannel)
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go doNothing(&wg, valueChannel)
    }

    for i := 0; i < 10; i++ {
        v := <- valueChannel

        fmt.Println(v)
    }
    wg.Wait()
}

func doNothing(wg *sync.WaitGroup, numChan chan int) {
    defer wg.Done()
    time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
    numChan <- 12
}

Upvotes: 0

Related Questions