タイトルの通りです。
基本的に繰り返し処理を書くと、処理の重さによって次の処理の開始が遅くなっていきます。
基本1
import time
import datetime as dt
while True:
start_datetime = dt.datetime.now()
print(f'START: {start_datetime}')
'''
何か処理
'''
time.sleep(10)
pass
# START: 2021-12-18 21:09:57.488152
# START: 2021-12-18 21:10:07.492113
# START: 2021-12-18 21:10:17.508288
# START: 2021-12-18 21:10:27.515364
# START: 2021-12-18 21:10:37.525368
# START: 2021-12-18 21:10:47.541210
# START: 2021-12-18 21:10:57.555242
何も対策を入れないと1ループで20ミリ秒遅れていきます。なので1日動かすと約3秒ズレることになり、これはゆゆしき問題です。
基本2
もし上記の繰り返しで重たい処理をしたらどうなるでしょう?
import time
import datetime as dt
def slow_process():
trash = 1
for idx in range(10000000):
trash = trash * idx
pass
pass
while True:
start_datetime = dt.datetime.now()
print(f'START: {start_datetime}')
'''
何か重たい処理
'''
slow_process()
time.sleep(10)
pass
# START: 2021-12-18 21:24:35.974505
# START: 2021-12-18 21:24:46.887488
# START: 2021-12-18 21:24:57.797534
# START: 2021-12-18 21:25:08.712103
# START: 2021-12-18 21:25:19.625054
# START: 2021-12-18 21:25:30.562727
# START: 2021-12-18 21:25:41.477604
このように1回のループで1秒ずつ遅れていきます。常に動かす必要がある処理だと、何か対策を入れないといけません。
対策
今回の問題は処理に時間がかかったにもかかわらず、スリープが10秒の固定なのが問題です。
処理にかかった時間を差し引いて、 スリープ時間を調整してあげれば常に一定で繰り返しさせることができます。
import time
import datetime as dt
def slow_process():
trash = 1
for idx in range(10000000):
trash = trash * idx
pass
pass
while True:
start_datetime = dt.datetime.now()
print(f'START: {start_datetime}')
'''
何か重たい処理
'''
slow_process()
now_datetime = dt.datetime.now()
next_datetime = start_datetime + dt.timedelta(seconds=10)
next_seconds = (next_datetime - now_datetime).seconds
# print(f"sleep: {next_seconds}sec")
time.sleep(next_seconds)
pass
# START: 2021-12-18 21:39:32.871239
# START: 2021-12-18 21:39:42.793268
# START: 2021-12-18 21:39:52.698554
# START: 2021-12-18 21:40:02.608381
# START: 2021-12-18 21:40:12.530488
# START: 2021-12-18 21:40:22.452134
# START: 2021-12-18 21:40:32.387661
このように、処理の開始時間と処理が終わった時間を差し引いて、スリープ時間を算出するようにすると、いくら重い処理をしても時間がズレることがなくなります。