Предисловие: Использование time.Timer
является рекомендуемым способом, использование time.After()
здесь только для демонстрации и обоснования. Пожалуйста, используйте второй подход.
Использование time.After()
(не рекомендуется для этого)
Если вы добавите time.After()
в ветвь case, это будет «сбрасываться» в каждой итерации, потому что каждый раз будет возвращаться новый канал, так что это работает:
func doingSomething(listenCh <-chan string) {
for {
select {
case mystr := <-listenCh:
log.Println("Received", mystr)
case <-time.After(1 * time.Second):
log.Println("Timeout")
return
}
}
}
(я использовал 1-секундный таймаут для тестирования на игровой площадке Go).
Мы можем проверить это как:
ch := make(chan string)
go func() {
for i := 0; i < 3; i++ {
ch <- fmt.Sprint(i)
time.Sleep(500 * time.Millisecond)
}
}()
doingSomething(ch)
Вывод (попробуйте на Go Playground ):
2009/11/10 23:00:00 Received 0
2009/11/10 23:00:00 Received 1
2009/11/10 23:00:01 Received 2
2009/11/10 23:00:02 Timeout
Использование time.Timer
(рекомендуемое решение)
Если есть высокая скорость приема от канала, это может быть немного бесполезной тратой ресурсов, так как новый таймер создается и используется под капотом time.After()
, который волшебным образом не останавливается и не собирает мусор немедленно когда он больше не нужен, если вы получаете значение из канала до истечения времени ожидания.
Более дружественным решением было бы создать time.Timer
перед циклом и сбросить его, если значение получено до истечения времени ожидания.
Вот как это будет выглядеть:
func doingSomething(listenCh <-chan string) {
d := 1 * time.Second
t := time.NewTimer(d)
for {
select {
case mystr := <-listenCh:
log.Println("Received", mystr)
if !t.Stop() {
<-t.C
}
t.Reset(d)
case <-t.C:
log.Println("Timeout")
return
}
}
}
Тестирование и вывод одинаковы. Попробуйте это на Go Playground .