@AdamSmith identified the problem. When main()
exits all goroutines are killed. There's no guarantee your other goroutines will finish before that, that's the nature of concurrency. Here's how you fix it.
First, let's make a couple changes to greet
. Have it sleep just a moment to make the problem more pronounced. We'll also have it accept a string rather than the channel, we'll see why in a moment.
func greet(str string) {
time.Sleep(100 * time.Millisecond)
fmt.Println("Hello " + str + "!")
}
Rather than creating a bunch of goroutines to read a fixed number of times from a channel, we want one goroutine which reads from the channel until it's exhausted. This is easiest done using range
. This takes full advantage of channels.
We also need a way to tell the main program to wait until the loop is done. This is easiest done with a second channel. More complex synchronization uses WaitGroups.
c := make(chan string, 2)
done := make(chan bool, 1)
go func() {
for str := range(c) {
greet(str)
}
done <- true
}()
The goroutine will read from c
until it's closed. Then it will send true
to the channel done
. Both are buffered to avoid deadlock due to blocking waiting to read or write to the channel.
Back in main
, we write to the channel, explicitly close it, and then wait to read from done
.
c <- "AAA"
c <- "BBB"
c <- "CCC"
c <- "DDD"
c <- "EEE"
close(c)
<-done
fmt.Println("main() stopped")
<-done
will block until there is something to read. This allows the goroutine to finish.
And bringing it all together.
package main
import(
"fmt"
"time"
)
func greet(str string) {
time.Sleep(100 * time.Millisecond)
fmt.Println("Hello " + str + "!")
}
func main() {
fmt.Println("main() started")
c := make(chan string, 2)
done := make(chan bool, 1)
go func() {
for str := range(c) {
greet(str)
}
done <- true
}()
c <- "AAA"
c <- "BBB"
c <- "CCC"
c <- "DDD"
c <- "EEE"
close(c)
<-done
fmt.Println("main() stopped")
}