Below is the example code in the Go programming book. I do not understand why the closer needs to be its own goroutine. I tried to move the closer into the main but it crashes. Somebody could explain why the closer needs to be in a separate goroutine?
Thanks!
func makeThumbnails(filenames <-chan string, result chan<- int64) int64 {
sizes := make(chan int64)
var wg sync.WaitGroup
for f := range filenames {
wg.Add(1)
go func(f string) {
defer wg.Done()
sizes <- int64(len(f))
}(f)
}
// **closer**, why this guy needs to be in a goroutine???
go func() {
wg.Wait()
close(sizes)
}()
var total int64
for size := range sizes {
total += size
}
result <- total
return total
}
The problem is that sizes
is not a buffered chan
, so only one of the anonymous goroutines can actually complete before sizes
needs to be read from. That makes wg.Wait()
wait forever (since the next goroutine is blocking on sizes <-
and can't defer wg.Done()
) and deadlocks.
By throwing the closer in a separate goroutine, it can close the sizes
chan whenever it's ready to do so, and process from sizes
in between. Ultimately this is a great use of a goroutine -- fire and forget closing!
To make this code work without the closer goroutine, you can simply initialize sizes
as a buffered chan with a buffer >= the length of filenames
.
func makeThumbnails(filenames <-chan string, result chan<- int64) int64 {
sizes := make(chan int64, 10) // buffered channel, now!
// if filenames sends more than 10 strings, though, we're in trouble!!
var wg sync.WaitGroup
for f := range filenames {
wg.Add(1)
go func(f string) {
defer wg.Done()
sizes <- int64(len(f))
}(f)
}
// **closer**, this guy doesn't need to be a goroutine!!
wg.Wait()
close(sizes)
var total int64
for size := range sizes {
total += size
}
result <- total
return total
}
However since filenames
's length is unknowable at runtime, it's not possible to do this easily. You'd have to read through filenames
, store it into a slice, then initialize sizes and for
over range filenamesSlice
and.... yeah basically you've just re-written the whole function at that point.