threading.Timer
имеет немного неуклюжий интерфейс (например, он не авто-повторяет каждые 30 минут, вам нужно снова его отпустить), и очевидный способ сделать это означает, что он постепенно затихает с течением времени) ,
Это также немного реалистично. если у вас много таймеров, у вас много потоков, что может быть проблемой для планировщика потоков вашей ОС.
Если у вас есть только один таймер, и вы работаете только каждые 30 минут, и все это для «фона», например, для опроса журнала и сводных писем, эти проблемы, вероятно, приемлемы - на самом деле, едва даже проблемы. Но есть проблемы, которые у вас есть.
Во-первых, выполнение функции finish = True
внутри функции создает локальную переменную с именем finish
, заслоняя любую глобальную переменную с тем же именем. Если вы хотите изменить глобальную переменную, необходимо global finish
заявление в верхней части функции. *
Кроме того, каждый раз, когда кто-то бьет /timely-req
, вы собираетесь выстрелить новый Timer
. Это означает, что я мог бы легко выполнить DoS-сервер, возможно, даже случайно, просто запросив URL-адрес несколько сотен раз. Итак, если вы собираетесь это сделать, вы, вероятно, захотите сделать его одиночным объектом и создать таймер, если он еще не существует. Существуют также десятки реализаций однопоточных многопользовательских планировщиков по вкладам PyPI, ActiveState и Flask, которые автоматически позаботятся об этой проблеме (а также о двух проблемах выше, что вам, вероятно, и не нужно).
Кроме того, когда вы указываете, что флажок ушел, он прекращает принимать запросы, позволяет вашему коду завершить существующие запросы, позволяет любому потоку, который вы создали, завершить, а затем завершает работу. Если у вас есть нить, которая никогда не заканчивается, это не сработает, поэтому, если вы хотите изящную функцию выключения, вы должны добавить ее самостоятельно (установка finish = True
).
И, наконец, не существует поточно-безопасной для обмена глобальной переменной между потоками без блокировки или другого устройства синхронизации потоков. В частности, теоретически возможно, что ваш поток сервера установит finish = True
, и ваш поток таймера будет продолжать видеть старое значение в кэше навсегда. На практике, по крайней мере, с CPython, вам это не удастся. ** Но лучше писать код правильно.
* Кроме того, Flask имеет дополнительную функцию, которая подделывает глобальные переменные с помощью локальных объектов, то есть каждый поток получает свою собственную копию finish
. И, поскольку каждый экземпляр Timer
представляет собой новый поток, настройка finish = True
не повлияет на него. Итак, если вы используете эту функцию, вы не можете использовать Timer
таким образом. Но вы, вероятно, не используете его, если не знаете об этом.
** В CPython, подмена глобального переменный является атомарной операцией, но не блокирован, но из-за GIL, это будет на практике всегда доступно для каждого другого потока с помощью следующей GIL-кванта времени, который является доли секунды, поэтому ваша 30-минутная задержка даже не заметит. В худшем случае, он будет запускаться еще раз, чем вам хотелось бы - что уже могло произойти, если щелчок на веб-странице занял несколько миллисекунд дольше, чем вы ожидали.
Я, наконец, установил 'apscheduler' для фоновых заданий, но не выполнил задание, которое я установил для его выполнения через 3 часа. Планировщик работает в том же процессе uwsgi, что и Flask, и я предполагаю, что uwsgi каким-то образом перезапускает его или что-то в этом роде. Я собираюсь запустить cron каждые 5 минут для выполнения необходимых своевременных задач. – licorna
@licorna: Должно быть довольно легко определить, перезапускается ли процесс 'uwsgi' сервером/контейнером. Если у него другой pid, он перезапускается. (Вы можете просматривать это извне с помощью 'ps' или просто записывать' os.getpid() 'так часто. Вы даже можете зарегистрировать что-то при запуске и использовать' atexit' для входа в систему при завершении работы. – abarnert