banner
肥皂的小屋

肥皂的小屋

github
steam
bilibili
douban

Python-定時実行タスク

起因#

最近、自動サインインスクリプトを書いており、サーバーに毎日 12 時に実行するためのタイミングの問題が発生しました。

この記事では、その解決方法について説明します。

xx 秒ごとにプログラムを実行する#

これは基本的な方法で、time.sleepを使用して実現できます。以下にプログラムを示します:

# 5秒ごとにプリント
from datetime import datetime
from time import sleep


def print_message():
    print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
    print("https://soapffz.com")


while True:
    print_message()
    sleep(5)

![][1]

これを元に、目標を試してみましょう:

from datetime import datetime
from time import sleep


def print_message():
    print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
    print("https://soapffz.com")


while True:
    while True:
        now = datetime.now()
        if now.hour == 0 and now.minute == 0:
            break
        sleep(20)
    print_message()

これはシンプルですが、このwhileループは常にCPUリソースを占有するため、一般的には使用しないことをお勧めします。

threading.Timer タイマー#

上記の方法はあまりにも過激なので、よりエレガントな方法に切り替えましょう。

threadingモジュールにはTimerクラスがあります。これは、タイマータスクを実行するために新しいスレッドを起動するため、ブロッキングされない関数です。

マルチスレッドを使用している場合は、スレッドセーフの問題に注意する必要があります。その場合は、threading.Timerモジュールを使用することができます。

Timer()関数は 3 つの引数を受け取ることができます:

Timer(10, task, ()).start()
タスクを実行するまでの遅延時間(秒単位)
実行するタスク、つまり関数
関数の呼び出しの引数(タプル)

xx 秒後にプログラムを実行し、一度だけ#

from datetime import datetime
from time import sleep
from threading import Timer


def print_message():
    print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
    print("https://soapffz.com")


Timer(10, print_message).start()

![][2]

xx 秒ごとにプログラムを実行し、継続的に実行する#

実際には、Timer()ステートメントを実行する関数に移動するだけです。

from datetime import datetime
from time import sleep
from threading import Timer


def print_message():
    Timer(10, print_message).start()
    print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
    print("https://soapffz.com")


print_message()

![][3]

同様に、24 時間ごとに(開始時間に関係なく)実行するには、Timer関数の時間を 86400 に変更するだけです。

schedule モジュールで定時実行タスクを実行する#

公式ドキュメント:https://schedule.readthedocs.io/en/stable/

これは軽量なスケジュールタスク実行ライブラリで、いくつかの例を見てみましょう:

from datetime import datetime
from time import sleep
import schedule


def print_message():
    print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
    print("https://soapffz.com")

schedule.every(15).seconds.do(print_message)
schedule.every().minutes.do(print_message)
schedule.every().hour.do(print_message)
schedule.every().day.at("10:30").do(print_message)
schedule.every(5).to(10).days.do(print_message)
schedule.every().wednesday.at("13:15").do(print_message)

while True:
    schedule.run_pending()
    sleep(1)

上記の意味は次のとおりです:

15秒ごとにタスクを実行する
1分ごとにタスクを実行する
1時間ごとにタスクを実行する
毎日10:30にタスクを実行する
5日から10日ごとにタスクを実行する
毎週水曜日のこの時刻にタスクを実行する
run_pending:実行可能なすべてのタスクを実行する

![][4]

実行結果に重複があることに注意してください。原因はわかりませんが、ディスカッションエリアで議論することができます。

schedule.run_pending () は何ですか?

schedule は実際にはタイマーです。while Trueの無限ループで、schedule.run_pending () は上記のタスクを継続的に実行するために schedule を実行します。タスクでは、異なる時間で実行することができます。これは、Linux で crontab タスクを設定するのと似ています。

したがって、schedule には制限があります。小規模な定時タスクを実行するためにのみ使用できます。制限はどこにあるのですか?

1. 実行する必要のあるジョブの関数は、無限ループタイプではないことが望ましいです。つまり、このスレッドには完了ポイントがある必要があります。スレッドがデッドロックすると、非常に困難な問題になります。また、次の定時タスクでは新しいスレッドが開始されるため、実行回数が多くなると問題が発生します。

2.schedule の時間間隔がジョブの実行時間よりも短い場合、スレッドが積み重なり、問題が発生します。つまり、ジョブの実行時間が 1 時間であり、スケジュールされたタスクが 5 分ごとに設定されている場合、スレッドが常に積み重なります。

また、sched.schedulerという別のライブラリもありますが、Timer()とほぼ同じ使い方です。興味がある場合は、自分で調べてみてください。

結論#

最後に、datetime+sleepの組み合わせの最初の方法とscheduleの 3 番目の方法を比較しました。

サーバーの設定:1 コア CPU、1838MB メモリ

最初の方法の通常の消費量は次のとおりです:

![][5]

3 番目の方法の通常の消費量は次のとおりです:

![][6]

後者の方が通常は優れているように見えますが、指定した時間になると

前者は安定しており、CPU をほとんど占有しませんが、後者は理由がわからず、すべてのメモリと CPU を占有し、多くのメッセージを送信します。

しかし、プログラムを確認しましたが、問題はありません。個人的には、前者を使用することをお勧めします。少し安定していますし、二重ループは非常に致命的です。

以上です。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。