In the classic Go worker pool pattern below, why is `close(jobs)` important, and what happens if it is omitted?
jobs := make(chan int, 100)
var wg sync.WaitGroup
for w := 0; w < 3; w++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := range jobs {
process(j)
}
}()
}
for _, j := range work {
jobs <- j
}
close(jobs)
wg.Wait()