質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

1回答

2771閲覧

schedule.run_pending()の実行順序

yuudai

総合スコア65

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

0クリップ

投稿2019/05/10 09:03

前提・実現したいこと

関数1と関数2を定義してどちらもscheduleで時間を設定します。先に関数2が実行されて関数2の中から関数1を実行したいです。
しかし最初のscheduleが先に実行されます。これは多分時間が若い方から実行されるからだと思います。それと関数2の中のschedule.run_pending()は実行されてないように感じました。
最後の方の
while True:
schedule.run_pending()
time.sleep(1)
の部分がschedule.every(1).minutes.do(fanc1)につながってしまっています。これをschedule.every(4).minutes.do(fanc2)の方につなげたいです。
イメージとしては最後の方の
while True:
schedule.run_pending()
time.sleep(1)

schedule.every(4).minutes.do(fanc2)
につながって次に
fanc2が実行されてその次にfanc1が実行されて、そこからは、fanc2のループでfanc1を定期的に実行すると行ったことがしたいです。
支離滅裂な文章ですが、よろしくお願いします。

発生している問題・エラーメッセージ

該当のソースコード

python

1import schedule 2import time 3import sys 4import datetime 5 6sys.setrecursionlimit(10000) 7 8def fanc1(): 9 print('aaaaaaa') 10 11 12schedule.every(1).minutes.do(fanc1) 13 14def fanc2(): 15 day_of_the_week = ["月","火","水","木","金","土","日"] 16 17 get_today = datetime.date.today() #今日の日付を取得 18 weekday = day_of_the_week[get_today.weekday()] #曜日に変換 19 20 if weekday == "土" or weekday == "日": 21 pass 22 else: 23 #機能 24 while True: 25 #11時50分になっていなかったら 26 schedule.run_pending() 27 time.sleep(1) 28 #11時50分を過ぎたら 29 break 30#テスト用にevery(4).minutesにしているが、実際はevery().day.at("00:00") 31schedule.every(4).minutes.do(fanc2) 32 33while True: 34 schedule.run_pending() 35 time.sleep(1)

試したこと

ここに問題に対して試したことを記載してください。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

scheduleの使用例をみると非常にシンプルです。

python

1import schedule 2import time 3 4schedule.every().day.at('00:00').do(lambda: print('new day has come')) 5 6if __name__ == '__main__': 7 while True: 8 schedule.run_pending() 9 time.sleep(1)

これを見たとき直感的に以下の前提をおきたくなります。

  • (前提1) 各Jobの中からrun_pendingは発行しない
  • (前提2) 各Jobは可能な限り短い時間で実行を終える

その前提を守って使う限りは簡単にものごとが進むと思います。しかし質問者さんは前提1を突破するようなコードを書いておられるので、可能かどうか判断するために本家サイトのドキュメント

https://schedule.readthedocs.io/en/stable/

のFAQをまず見ると・・・

https://schedule.readthedocs.io/en/stable/faq.html#how-can-i-make-sure-long-running-jobs-are-always-executed-on-time

How can I make sure long-running jobs are always executed on time?

Schedule does not account for the time it takes the job function to execute. To guarantee a stable execution schedule you need to move long-running jobs off the main-thread (where the scheduler runs).
...

とあります。どうもFAQでは前提1を突破できるか(スケジュールされたJobからさらに別のJobをスケジュールすることができるか)判断するのは難しそう。FAQの記述は「スケジューリングはメインスレッドの一か所で行う」前提で書かれているように見えるからです。

そこでさらにAPIドキュメントを見てみましたが結論からいえば前提1の突破はできると思いました。

scheduleパッケージにはSchedulerとJobというクラスがあります。それらは文字通りの意味です。schedule.~.do(func)とする典型的な使い方ではそれらのクラスは直接見えてきませんが、Schedulerインスタンスを複数明示的に生成すればJobの入れ子が可能であると思いました。

sample.py

Python

1import schedule 2import time 3import datetime 4 5 6scheduler1 = schedule.Scheduler() 7scheduler2 = schedule.Scheduler() 8 9 10def func1(): 11 now = datetime.datetime.now() 12 print(f'{now}: func1') 13 14 15scheduler1.every(5).seconds.do(func1) 16 17 18def func2(): 19 now = datetime.datetime.now() 20 print(f'{now}: func2 invoked') 21 if now.minute % 2 == 0: 22 # 偶数分では何もしない 23 pass 24 else: 25 # 奇数分では00秒~30秒の間だけfunc1をスケジュールする 26 while True: 27 now = datetime.datetime.now() 28 if now.second % 60 < 30: 29 scheduler1.run_pending() 30 time.sleep(1) 31 else: 32 break 33 print(f'{now}: func2 stopped') 34 35 36scheduler2.every().minute.at(':00').do(func2) 37 38 39if __name__ == '__main__': 40 while True: 41 scheduler2.run_pending() 42 time.sleep(1)

==>

sh

1$ python3 sample.py 22019-05-11 05:01:00.033183: func2 invoked 32019-05-11 05:01:00.033183: func1 42019-05-11 05:01:05.036461: func1 52019-05-11 05:01:10.038144: func1 62019-05-11 05:01:15.040443: func1 72019-05-11 05:01:20.042950: func1 82019-05-11 05:01:25.045956: func1 92019-05-11 05:01:30.049446: func2 stopped 102019-05-11 05:02:00.065099: func2 invoked 112019-05-11 05:02:00.065099: func2 stopped 12...

(確認の都合上インターバルの条件を変えています。)


ところで前提1を守りスケジューリングを入れ子にせずに書くこともできましょう。フラグを用いた状態遷移は「あまりよくない」気もしますが、そうまで複雑でないならこの実装もありな気がします。スケジューラーを入れ子にするのとフラグによる制御のどちらが望ましいか・・・意見がわかれるかも知れません。

python

1import schedule 2import datetime 3import time 4 5 6def seconds_of_day(): 7 td = datetime.datetime.now() - datetime.datetime(1, 1, 1) 8 return td.seconds 9 10 11is_weekday = datetime.date.today().weekday() < 5 12in_time = seconds_of_day() < 11 * 3600 + 50 * 60 + 0 13 14 15def func(): 16 if not (is_weekday and in_time): 17 return 18 # 何かする 19 pass 20 21 22schedule.every().monday.at('00:00').do(lambda: is_weekday = True) 23schedule.every().saturday.at('00.00').do(lambda: is_weekday = False) 24schedule.every().day.at('00:00').do(lambda: in_time = True) 25schedule.every().day.at('11:50').do(lambda: in_time = False) 26schedule.every().minute.do(func) 27 28if __name__ == '__main__': 29 while True: 30 schedule.run_pending() 31 time.sleep(1)

質問意図との関係がはっきりしませんが、

sys.setrecursionlimit(10000)

この行が気になりました。例えば再帰アルゴリズムの練習のために階乗の実装例として以下のようなものをよく見ます。これは数学的に美しい実装ではありますが、必ずしも実用的とはいえません。(言語システムの実装者にとってtail recursionの変換といった興味深いトピックではあるかも知れませんが、アプリケーションを書く立場ではこれが再帰呼び出しになるかどうか心配するよりは避けて通りたいです)

python

1def factorial(n): 2 if n == 0: 3 return 1 4 else: 5 return n * factorial(n - 1)

もしそんなに呼び出しの入れ子が深くなる心配があるなら例え10000まで許したとしてもいつその限界を突破してしまうかわかったものではないため不安になります。本件のように長期間動かし続けるプログラムならなおのことやりたくありません。

投稿2019/05/10 20:25

編集2019/05/10 20:30
KSwordOfHaste

総合スコア18394

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yuudai

2019/05/12 02:46

ありがとうございました。一つ目に挙げていただいた方法で無事に解決することができました。sys.setrecursionlimit(10000)についてはjupyter notebookでこのコードを試していた時にrecursion errorが出てしまったのでそれについて調べていたらsys.setrecursionlimit(10000)で突破できるのかなと思って一応書いていたんですけど、今ではrecursion errorは出なくなったのでその行は消しました。 回答していただき本当にありがとうございました。
KSwordOfHaste

2019/05/12 03:09

> recursion errorが出てしまったので なるほどです。 きっとfunc2がモジュールトップのrun_pendingではなく自分自身のrun_pendingから起動されたため呼び出しスタックが深くなりすぎたのだろうと思います。最初の例のようにすればそういうことはなくなるのでスタックオーバーフロー問題も解消されたと見てよいでしょう。
yuudai

2019/05/12 04:51

私もそう思います。この度はありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問