起因#
最近、自動サインインスクリプトを書いており、サーバーに毎日 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 を占有し、多くのメッセージを送信します。
しかし、プログラムを確認しましたが、問題はありません。個人的には、前者を使用することをお勧めします。少し安定していますし、二重ループは非常に致命的です。
以上です。