Как выполнить действие через промежуток времени, который может измениться?
Есть общий цикл, обрабатывающий все запросы пользователей, и есть понятие сессии, по истечению которой, должно выполниться действие, предположим (очень близко по смыслу) пользователю должно отправиться уведомление. Сессия закрывается по истечению определенного времени после последнего запроса от пользователя, то есть это отложенное выполнение, но время должно обновляться, так как отсчет должен идти от последнего запроса. У меня есть несколько вариантов, например, функция отложенного выполнения просто завершается, если по времени последнего запроса пользователя понимает, что оно обновилось и где-то еще есть другая функция, которая сделает что требуется, но несколько позже, но, как мне кажется, это далеко не самое изящное решение и существуют более оптимальные способы добиться нужного поведения. Собственно, предложения?
Можно использовать функцию time. AfterFunc для запуска функции после определенного промежутка. Она возвращает таймер. Если произошел еще один запрос, то текущий таймер останавливать с помощью Stop и создавать такой же новый.
Я думал об этом, сложно объяснить почему отказался, собственно и не буду.. Но это конкретно в данном проекте, в других подобных задачах это эффективное решение. В дополнение, я не знал, что time.AfterFunc создает ссылку на таймер, не знал что он вообще работает поверх него, за что отдельное спасибо.
Было решено сделать сессию полноправным объектом (до этого было просто поле с временем последнего действия) с методом Close:
func (s *Session) Close(timeout time.Duration) {
d := s.LastActionTime.Add(timeout).Sub(time.Now())
for d > 0 {
time.Sleep(d)
d = s.LastActionTime.Add(timeout).Sub(time.Now())
}
// Closing the Session
}
Сложностью встроить данный метод конкретно в этот проект, с его взаимосвязями объектов и прочим. Использование time.AfterFunc потребовало бы гораздо большей реорганизации кода. В целом да, это решение.
В дополнение к решению Алексея, предложу ещё вариант, который применяю лично я для удаления протухших сессий:
Т.к. высокая точность не требуется, то раз в минуту я просто перебираю все сессии, смотрю на время последнего обращения, и если прошло больше установленного времени, то сессию прибиваю. Соответственно, при каждом обращении за сессией, поле LastRequest устанавливается на текущее время.
Да, спасибо, подобное было одним из моих "несколько вариантов". Я не хочу говорить за производительность, не думал в эту сторону, в плане каково оно регулярно бегать по всем сессиям за место потока и сна для каждой, но как минимум чисто из соображений эстетичности мне данный вариант не сильно понравился, но он естественно имеет право на жизнь.