web-dev-qa-db-ja.com

Python 3で繰り返しイベントをスケジュールする

Python 3.で毎分実行する繰り返しイベントをスケジュールしようとしています。

クラスsched.schedulerを見てきましたが、別の方法があるかどうか疑問に思っています。このために複数のスレッドを使用できるという話を聞いたことがありますが、それは気にしません。

基本的に、JSONを要求してから解析します。その値は時間とともに変化します。

sched.schedulerを使用するには、1時間の実行をスケジュールするように要求するループを作成する必要があります。

scheduler = sched.scheduler(time.time, time.sleep)

# Schedule the event. THIS IS UGLY!
for i in range(60):
    scheduler.enter(3600 * i, 1, query_rate_limit, ())

scheduler.run()

これを行う他の方法は何ですか?

29
Humphrey Bogart

threading.Timer を使用できますが、スケジューラオブジェクトの.enterメソッドと同様に、1回限りのイベントもスケジュールします。

1回限りのスケジューラーを定期的なスケジューラーに変換する通常のパターン(任意の言語)は、指定された間隔で各イベントを再スケジュールします。たとえば、schedを使用すると、あなたがしているようなループを使用するのではなく、次のようなループを使用します。

def periodic(scheduler, interval, action, actionargs=()):
    scheduler.enter(interval, 1, periodic,
                    (scheduler, interval, action, actionargs))
    action(*actionargs)

コールで「永久に定期的なスケジュール」全体を開始します

periodic(scheduler, 3600, query_rate_limit)

または、threading.Timerの代わりにscheduler.enterを使用できますが、パターンは非常に似ています。

より洗練されたバリエーションが必要な場合(たとえば、特定の時間または特定の条件で定期的な再スケジュールを停止する場合)、いくつかの追加パラメーターで対応するのはそれほど難しくありません。

38
Alex Martelli

私の謙虚な件名は次のとおりです。

_from threading import Timer

class RepeatedTimer(object):
    def __init__(self, interval, function, *args, **kwargs):
        self._timer     = None
        self.function   = function
        self.interval   = interval
        self.args       = args
        self.kwargs     = kwargs
        self.is_running = False
        self.start()

    def _run(self):
        self.is_running = False
        self.start()
        self.function(*self.args, **self.kwargs)

    def start(self):
        if not self.is_running:
            self._timer = Timer(self.interval, self._run)
            self._timer.start()
            self.is_running = True

    def stop(self):
        self._timer.cancel()
        self.is_running = False
_

使用法:

_from time import sleep

def hello(name):
    print "Hello %s!" % name

print "starting..."
rt = RepeatedTimer(1, hello, "World") # it auto-starts, no need of rt.start()
try:
    sleep(5) # your long-running job goes here...
finally:
    rt.stop() # better in a try/finally block to make sure the program ends!
_

特徴:

  • 標準ライブラリのみ、外部依存関係なし
  • Alex Martnelliによって提案されたパターンを使用します
  • start()およびstop()は、タイマーが既に開始/停止している場合でも、複数回呼び出しても安全です。
  • 呼び出される関数は、位置引数と名前付き引数を持つことができます
  • intervalはいつでも変更でき、次回の実行後に有効になります。 argskwargs、さらにはfunctionでも同じです!
18
MestreLion

schedule を使用できます。 Python 2.7および3.3で動作し、かなり軽量です。

import schedule
import time

def job():
   print("I'm working...")

schedule.every(10).minutes.do(job)
schedule.every().hour.do(job)
schedule.every().day.at("10:30").do(job)

while 1:
   schedule.run_pending()
   time.sleep(1)
15
dbader

Celery を使用します。

from celery.task import PeriodicTask
from datetime import timedelta


class ProcessClicksTask(PeriodicTask):
    run_every = timedelta(minutes=30)

    def run(self, **kwargs):
        #do something
6
user

MestreLionの回答に基づいて、マルチスレッドの小さな問題を解決します。

from threading import Timer, Lock


class Periodic(object):
    """
    A periodic task running in threading.Timers
    """

    def __init__(self, interval, function, *args, **kwargs):
        self._lock = Lock()
        self._timer = None
        self.function = function
        self.interval = interval
        self.args = args
        self.kwargs = kwargs
        self._stopped = True
        if kwargs.pop('autostart', True):
            self.start()

    def start(self, from_run=False):
        self._lock.acquire()
        if from_run or self._stopped:
            self._stopped = False
            self._timer = Timer(self.interval, self._run)
            self._timer.start()
            self._lock.release()

    def _run(self):
        self.start(from_run=True)
        self.function(*self.args, **self.kwargs)

    def stop(self):
        self._lock.acquire()
        self._stopped = True
        self._timer.cancel()
        self._lock.release()
6
fdb

Advanced Python Scheduler を使用できます。cronのようなインターフェースさえあります。

6
jordixou

Threadを使用した高速でダーティな非ブロッキングループを次に示します。

#!/usr/bin/env python3
import threading,time

def worker():
    print(time.time())
    time.sleep(5)
    t = threading.Thread(target=worker)
    t.start()


threads = []
t = threading.Thread(target=worker)
threads.append(t)
t.start()
time.sleep(7)
print("Hello World")

特別なことは何もありません。workerは、それ自体の新しいスレッドを遅延して作成します。最も効率的ではないかもしれませんが、十分に単純です。 northtree's answer は、より洗練されたソリューションが必要な場合の方法です。

this に基づいて、Timerだけで同じことができます。

#!/usr/bin/env python3
import threading,time

def hello():
    t = threading.Timer(10.0, hello)
    t.start()
    print( "hello, world",time.time() )

t = threading.Timer(10.0, hello)
t.start()
time.sleep(12)
print("Oh,hai",time.time())
time.sleep(4)
print("How's it going?",time.time())
2

Alex Martelliの回答に基づいて、統合がより簡単なdecoratorバージョンを実装しました。

import sched
import time
import datetime
from functools import wraps
from threading import Thread


def async(func):
    @wraps(func)
    def async_func(*args, **kwargs):
        func_hl = Thread(target=func, args=args, kwargs=kwargs)
        func_hl.start()
        return func_hl
    return async_func


def schedule(interval):
    def decorator(func):
        def periodic(scheduler, interval, action, actionargs=()):
            scheduler.enter(interval, 1, periodic,
                            (scheduler, interval, action, actionargs))
            action(*actionargs)

        @wraps(func)
        def wrap(*args, **kwargs):
            scheduler = sched.scheduler(time.time, time.sleep)
            periodic(scheduler, interval, func)
            scheduler.run()
        return wrap
    return decorator


@async
@schedule(1)
def periodic_event():
    print(datetime.datetime.now())


if __name__ == '__main__':
    print('start')
    periodic_event()
    print('end')
2
northtree

私のサンプルを見る

import sched, time

def myTask(m,n):
  print n+' '+m

def periodic_queue(interval,func,args=(),priority=1):
  s = sched.scheduler(time.time, time.sleep)
  periodic_task(s,interval,func,args,priority)
  s.run()

def periodic_task(scheduler,interval,func,args,priority):
  func(*args)
  scheduler.enter(interval,priority,periodic_task,
                   (scheduler,interval,func,args,priority))

periodic_queue(1,myTask,('world','hello'))
0
Vladimir Avdeev