Preventing goroutine leaks

Just a guy who loves to write code and watch anime.
The done Channel Pattern
func doWork(done <-chan interface{}) <-chan interface{} {
terminated := make(chan interface{})
go func() {
defer close(terminated)
for {
select {
case s := <-strings:
// Do work
case <-done: // Parent says "stop working"
return
}
}
}()
return terminated
}
Key insight: Pass a done channel to every goroutine so the parent can signal cancellation.
Preventing Goroutine Leaks
Without cancellation (bad):
newRandStream := func() <-chan int {
randStream := make(chan int)
go func() {
for {
randStream <- rand.Int() // runs forever!
}
}()
return randStream
}
With cancellation (good):
newRandStream := func(done <-chan interface{}) <-chan int {
randStream := make(chan int)
go func() {
defer close(randStream)
for {
select {
case randStream <- rand.Int():
case <-done:
return // clean exit
}
}
}()
return randStream
}
for-select Loop Pattern
This pattern shows up everywhere in Go:
for {
select {
case <-done:
return
default:
// do non-preemptable work
}
}
Two variations:
With default: Non-blocking, checks done channel between work
Without default: Blocking, waits for channels






