При отмене родительского context (вызов cancel-функции или истечение таймаута) каскадно отменяются все дочерние context'ы: канал ctx.Done() закрывается у родителя и у всех производных. Горутины, слушающие ctx.Done(), получают сигнал и могут завершиться.
ctx, cancel := context.WithCancel(parent)
defer cancel()
go func() {
select {
case <-ctx.Done():
return // родитель отменили - выходим
case result := <-doWork():
use(result)
}
}()
Дочерний context может быть отменён раньше родительского (например, WithTimeout с меньшим временем). Отмена родителя всегда отменяет дочерние - это основа graceful shutdown: отменили корневой context - все горутины получают Done().