⌛ runtime: special case timer channels.
У таймеров в Go есть интересная особенность: если на них не вызвать метод Stop
то сборщик мусора не соберет их пока они не закончат свою работу или не будут явно остановлены. И если в случае функции time.After
(которая возвращает канал) время сборки мусора отодвигалось до срабатывания таймера, то тикер time.Tick
(функция которая тоже возвращает канал) не будет собран сборщиком мусора вообще никогда. Документация, конечно, объясняет эти моменты, но хотелось бы, что-бы рантайм делал свою работу а не оставлял мусор из-за особенностей работы языка. В частности из-за этой особенности time.Tick
невозможно было применять в реальных долгоживущих приложениях.
Сейчас корректная работа с таймерами выглядит примерно вот так:
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
select {
// some other cases
// ...
case t := <-ticker.C:
fmt.Println("Current time: ", t)
return
}
}
Issue которое описывает проблему и потенциальное решение существует уже почти 10 лет. Более того, Расс хотел придумать и внедрить решение еще в Go 1.7. Однако время шло и к проблеме никто не возвращался.
В июле 2023го (спустя почти 9 лет) Расс наконец предложил решение в новом issue. Решение не успевало попасть в Go 1.21, но была надежда, что оно попадет в Go 1.22. Более того, в августе решение было одобрено, а так как PR уже был готов, была мысль, что оно попадет в мастер как можно скорее. Однако время продолжало идти, а PR висел и висел. К своему смущению, я был уверен, что проблема была решена в 1.22.
И вот наконец, момент настал: https://github.com/golang/go/commit/508bb17edd04479622fad263cd702deac1c49157. И даже документация теперь отражает, что вызов
Stop
на таймерах и тикерах больше не обязателен.Ну что, как говорится «лучше поздно, чем никогда». Теперь
time.Tick
можно использовать в горячих местах. Еще одно место для легких правок в 1.23.П.С. Если вам зачем-то нужно старое поведение, то переменная окружения
GODEBUG=asynctimerchan=1
даст вашей программе старый вариант.
>>Click here to continue<<