Yesterday we looked at some sample code with goroutines:
for i := range 10 {
go func() {
fmt.Println(i)
}()
}
But if you ran it, you probably saw no output. This is because the main goroutine exits before the 10 new goroutines have a chance to run. How do we solve this?
We need some sort of synchronization mechanism in place, to ensure we wait for all goroutines to finish before exiting the program. We’re going to look at severaly ways to do this over the coming days. For now, we’ll look at how to handle this ourselves, without any special packages.
Using a channel
A simple way to do this is to use a channel. We can create a channel that we will use to signal when a goroutine has finished. Here’s how we can modify the code:
done := make(chan struct{})
for i := range 10 {
go func(i int) {
fmt.Println(i)
done <- struct{}{} // Signal that this goroutine is done
}(i)
}
// Wait for all goroutines to finish
for range 10 {
<-done
}
Now if you run the code, you should see the numbers 0 through 9 printed in random order, as the main goroutine waits for all the spawned goroutines to finish before exiting.
Let’s look at how this works.
We’ve created a channel called done
. The type of the channel doesn’t matter, since we’re using it merely to signal completion. I chose an empty struct (struct{}
), as this does a good job of conveying the semantic meaning of a signal-only channel without carrying any data.
We’ve updated the anonymous function called by each new goroutine to send a value to the done
channel when it finishes its work. Then the main goroutine runs a separate loop that waits until it has received 10 values (“done” signals) on the done
channel before it exits.
Note that technically it’s still possible for one or more goroutines to still be running in when the main goroutine exits. That’s because there may be a small lag between the time the ‘done’ value is sent on the channel, and the time the goroutine is cleaned up and exits. However, in practice, we don’t care—all we care about is that the work of the goroutine is completed before the main goroutine exits.