2

How do I wait for all go routines to finish and then read all the data from a channel?

Why is this example stuck waiting for the go routines to finish?

Go Playground

package main

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

func doStuff(i int, wg *sync.WaitGroup, messages chan<- string) {
    defer wg.Done()
    time.Sleep(time.Duration(i) * time.Second)
    messages <- fmt.Sprintf("Doing stuff...%d", i)

}

func doMoreStuff(i int, wg *sync.WaitGroup, messages chan<- string) {
    defer wg.Done()
    time.Sleep(time.Duration(i) * time.Second)
    messages <- fmt.Sprintf("Doing more stuff...%d", i)
}

func main() {
    var wg sync.WaitGroup
    var messages = make(chan string)

    for start := time.Now();; {
        elapsedTime := time.Since(start)
        fmt.Println(elapsedTime)
        if elapsedTime > time.Duration(3) * time.Second {
            fmt.Println("BREAK")
            break
        }

        for i := 0; i < 10; i++ {
            wg.Add(1)
            go doStuff(i, &wg, messages)

            wg.Add(1)
            go doMoreStuff(i, &wg, messages)
        }
        time.Sleep(time.Duration(1) * time.Second)
    }
    fmt.Println("WAITING")
    wg.Wait()
    fmt.Println("DONE")
    for message := range messages {
        fmt.Println(message)
    }
}
1
  • 1
    This defeats the purpose of a channel entirely - channels are for concurrency, and you're explicitly trying to write and read serially. Just have the writers append to a slice (using a Mutex to avoid concurrent writes), then when they all finish, use the slice to get the results.
    – Adrian
    Feb 11, 2020 at 15:15

2 Answers 2

4

If you want to wait for all goroutines to finish that send messages on a channel, and you want to start reading the messages after that, then you have no choice but to use a buffered channel that can "host" all the messages that can be sent on it by the goroutines.

This isn't something practical. And even if you'd go down this path, you would be able to receive and print the messages, but the for range loop doing so would never terminate because that only terminates when it receives all messages that were sent on the channel before it was closed, but you never close the channel. You may check this "half-working" solution on the Go Playground.

Instead launch a goroutine that waits for others to finish, and then close the channel:

go func() {
    fmt.Println("WAITING")
    wg.Wait()
    close(messages)
}()

So now you may use for range to receive messages in the main goroutine:

for message := range messages {
    fmt.Println(message)
}

fmt.Println("DONE")

Try this one on the Go Playground.

This solution is still not perfect: it first has to launch all goroutines, and only then it tries to receive values, and all the launched goroutines will be blocked on their send operation (until main is ready to receive those).

A better solution may be to launch a goroutine that receives the values, preferably before the goroutines that send messages on the channel (else they would be blocked on their sends anyway):

go func() {
    for message := range messages {
        fmt.Println(message)
    }
}()

And wait for the goroutines and close the channel at the end of main():

fmt.Println("WAITING")
wg.Wait()
close(messages)

The problem with this is that when main() ends, so does your app, it does not wait for other non-main goroutines to finish. And this means it will not wait for the "consumer" goroutine to receive the messages.

To wait for that "consumer", you may use an additional sync.WaitGroup:

var wg2 sync.WaitGroup
wg2.Add(1)
go func() {
    defer wg2.Done()
    for message := range messages {
        fmt.Println(message)
    }
}()

// And the end of `main()`:

fmt.Println("WAITING")
wg.Wait()
close(messages)

fmt.Println("DONE")
wg2.Wait()

Try this one on the Go Playground.

0

The example is stuck because you are using an unbuffered channel. Both go-routines are blocked waiting to write to the channel since nothing is ready to read from it.

You can use a buffered channel but then you would just be using it as a data storage. Channels are more useful for communication. The question is why do you want to wait until all the writers are finished? In the general case of an unknown (unlimited) number of writers you would no know how big to make your channel.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.