There are two different approaches to clean up a goroutine.
-
Use a kill channel to signal cancellation and a done channel to indicate that goroutine has been terminated.
type Worker struct { Done chan struct{} Kill chan struct{} Jobs chan Job } func (w *Worker) Run() { defer func() { w.Done <- struct{}{} } for { select { case <-w.Kill: return case j := <-w.Jobs: // Do some work } } go w.Run() w.Kill <- struct{}{}
-
Use
context
to canceltype Worker struct { Ctx context.Context Cancel context.CancelFunc Jobs chan Job } func (w *Worker) Run() { for { select { case <-w.Ctx.Done(): return case j := <-w.Jobs: // Do some work } } go w.Run() w.Cancel()
What are the pros/cons of each approach? Which one should I default to?
I understand that if I want to kill a tree of interconnected goroutines, I should go with context approach but let's just say I have a simple worker that doesn't start other goroutines internally.