Search code examples
multithreadinggodeadlockchannelgoroutine

How can I have a single goroutine function that waits for values from multiple others?


I've created a Go playground sample that illustrates what I'm talking about.

In the example, I create one goroutine, func2, which I would simply like to wait for inputs through its channel and print the value once it arrives.

func func2(ch chan int) {
    fmt.Println("func2")
    v:=<-ch
    fmt.Println(v)
}

Then, in a loop, I create goroutines for another function, and these are part of a WaitGroup.

func func1(ch chan int, wg *sync.WaitGroup) {
    fmt.Println("func1")
    ch <- 11032
    wg.Done()
}

And in main, I wait for the WaitGroup. I'm running into a deadlock and I'm not sure how to fix it. Just so it's clear what I'm trying to achieve, I want func2 to stay open as a thread once I call it, to process n values, where n is the number of goroutines I call for func1. I considered using a WaitGroup Wait inside of func2, but I don't want it blocking, as it needs to process new data from func1 as it's sent.


Solution

  • I think you're getting a deadlock because your func2 is consuming only 1 value from ch and then finishing. Then the other func1 goroutines are stuck waiting for ch to be available to write into, which they cannot do because there is no other goroutine to read from ch on the other end.

    Since you want func2 to continually consume values from ch until ch is closed, you need to create a loop in func2 like so:

    func func2(ch chan int) {
        fmt.Println("func2")
        for v := range ch {
            fmt.Println(v)
        }
    }
    

    This will keep func2 "alive" and reading from ch until you do close(ch) somewhere else. The appropriate place to close ch in your example would likely be in main after wg.Wait().

    If you want to be sure to see the results from all Println statements before the program finishes, you should also use some synchronization mechanism to wait for func2 to finish. Otherwise main will end immediately after close(ch) which may or may not be before func2 has printed every value it received.

    A common technique for this is a "done" channel. For example:

    func func2(ch chan int, done chan bool) {
        fmt.Println("func2")
        for v := range ch {
            fmt.Println(v)
        }
        done <- true
    }
    

    and in main:

    done := make(chan bool)
    go func2(ch, done)
    ...
    wg.Wait()
    close(ch)
    <-done
    

    Using chan struct{} (empty struct) is also very common since empty struct requires no memory.