Search code examples
gogoroutinewaitgroup

Deadlock - all goroutines are asleep (even when using wait groups)


I am learning go concurrency and I want two go routines to keep talking to each other while passing along updated values via channels. One add 2 to the number while the other subtracts 1. The code and the output are given below:

What's wrong with this code?

package main

import (
    "fmt"
    "sync"
    "time"
)

var wg sync.WaitGroup

func addTwo(r chan int, e chan int) {
    val := <-r
    fmt.Println("AT Recieved: ", val)
    time.Sleep(1 * time.Second)
    e <- val + 2
}

func subtractOne(r chan int, e chan int) {
    val := <-r
    fmt.Println("SO Recieved: ", val)
    time.Sleep(1 * time.Second)
    e <- val - 1
}

func main() {
    ch := make(chan int)
    ch2 := make(chan int)

    wg.Add(1)
    go addTwo(ch, ch2)

    wg.Add(1)
    go subtractOne(ch2, ch)

    ch <- 1
    wg.Wait()

}

Output:

AT Recieved:  1
SO Recieved:  3
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0x4b2de8?)
    /usr/lib/go-1.18/src/runtime/sema.go:56 +0x25
sync.(*WaitGroup).Wait(0x0?)
    /usr/lib/go-1.18/src/sync/waitgroup.go:136 +0x52
main.main()

And then it just exits.

Why aren't the goroutines exchanging values forever, even when I am not calling wg.Done() in the goroutines?


Solution

  • Both your launched goroutines just receive one value and send one, then they end. From there on there's only the main goroutine, blocked at wg.Wait() since you never call wg.Done().

    You forgot to use a(n endless) loop:

    func addTwo(r chan int, e chan int) {
        for {
            val := <-r
            fmt.Println("AT Recieved: ", val)
            time.Sleep(1 * time.Second)
            e <- val + 2
        }
    }
    
    func subtractOne(r chan int, e chan int) {
        for {
            val := <-r
            fmt.Println("SO Recieved: ", val)
            time.Sleep(1 * time.Second)
            e <- val - 1
        }
    }
    

    With this change your app goes on forever, output being:

    AT Recieved:  1
    SO Recieved:  3
    AT Recieved:  2
    SO Recieved:  4
    AT Recieved:  3
    SO Recieved:  5
    AT Recieved:  4
    ....