Ключевым моментом здесь является понимание того, как ведет себя выборка, если может выполняться несколько случаев, а именно псевдослучайно:
Если одно или несколько сообщений может продолжаться, одно из возможных сообщений выбирается с помощью равномерного псевдослучайного выбора.
https://golang.org/ref/spec#Select_statements
select {
case <- stopCh:
return
case dataCh <- rand.Intn(Max):
}
Только с этим вторым оператором выбора после закрытия stopCh
возможно, что оба случая могут продолжаться, если хотя бы одно из следующих условий верно:
- dataCh буферизуется и являетсяне в емкости
- по крайней мере одна попытка получения от
dataCh
, даже после закрытия stopCh
Без проверки stopCh
явно это возможно (хотя и маловероятно)что среда выполнения повторно выбирает второй случай, даже если ожидается, что программа будет завершена.Если в каждой итерации запускается ракета, вы можете увидеть, как это может быть проблемой.
Если оба эти условия могут быть положительно исключены, первое утверждение select может быть опущено, поскольку невозможнооба случая готовы продолжить.В статье Go101 просто показано решение, которое гарантированно работает, не делая никаких предположений.
Этот шаблон не является редкостью в реальном коде и обычно связан с отменой контекста:
func f(ctx context.Context, ch chan T) {
for {
// Make sure we don't shoot after ctx has been
// canceled, even if a target is already lined up.
select {
case <-ctx.Done():
return
default:
}
// Or, equivalently: if ctx.Err() != nil { return }
select {
case <-ctx.Done():
return
case t := <-ch:
launchMissileAt(t)
}
}
}