Pythonで定期実行(周期処理)を実装!threading.Timerの正しい使い方と注意点

この記事は約5分で読めます。

Pythonでプログラムを作成している際、「5秒おきにセンサーの値を読み取りたい」「メインの処理を止めずに、裏側で1分ごとにログを出力したい」といった、定期実行(周期処理)が必要になる場面は多いですよね。

特に通信アプリや組み込み系の制御ソフトを制作していると、周期的なイベント制御は必須のテクニックです。

Pythonでこれを実現する方法はいくつかありますが、標準ライブラリの threading.Timer を使うのが最も手軽で強力です。本記事では、初心者の方でもすぐに実装できる基本形から、実戦で役立つ「クラス化による管理術」まで、詳しく解説します。

1. Pythonで定期実行を実現する主な手法

まず、定期実行にはいくつかの方法があります。自分の目的に合っているか確認しましょう。

手法難易度メイン処理特徴
while + sleep停止する最も簡単だが、待機中は他の処理ができない。
threading.Timer動く標準機能のみで完結。非同期で裏側で動かせる。
scheduleライブラリ動く直感的に書けるが、外部ライブラリのインストールが必要。

「メインのループは回し続けたいけれど、裏で定期的なタスクも走らせたい」という場合は、今回紹介する threading.Timer が最適です。

2. threading.Timerとは?

threading.Timer は、Pythonの標準ライブラリ threading モジュールに含まれるクラスです。

指定した時間が経過した後に、特定の関数(コールバック関数)を別のスレッドで実行してくれます。「スレッド」を使用するため、タイマーが待機している間も、メインのプログラムは止まらずに動作し続けるのが最大のメリットです。

3. 【基本】threading.Timerで1回だけ実行する

まずは最もシンプルな「◯秒後に1回だけ実行する」コードを見てみましょう。

import threading

def hello():
    print("3秒経過しました!")

# 1. タイマーのインスタンス作成 (秒数, 実行する関数)
t = threading.Timer(3.0, hello)

# 2. タイマー開始
t.start()

print("タイマー開始後、すぐにこの行が実行されます(非同期)")

4. 【実践】Timerを「周期的に」動かす再帰の実装方法

threading.Timer はデフォルトでは「1回きり」の動作です。これを周期的に動かすには、実行される関数の中でもう一度タイマーをセットする(再帰呼び出し)というテクニックを使います。

サンプルコード

import threading
import time

def periodic_task():
    print(f"実行時刻: {time.ctime()}")
    
    # 処理が終わったら、再度タイマーをセットして起動(1秒周期)
    global timer
    timer = threading.Timer(1.0, periodic_task)
    timer.start()

# 初回の起動
periodic_task()

# 5秒間メイン処理を走らせる
try:
    while True:
        time.sleep(1)
except KeyboardInterrupt:
    print("停止中...")
    timer.cancel() # タイマーを止める

5. 【応用】クラス化して可読性と再利用性を高める

実戦のアプリ開発では、グローバル変数を使うよりもクラスとして定義したほうが、タイマーの開始・停止が管理しやすくなります。

コピペで使える「周期タイマークラス」

import threading
import time

class RepeatingTimer(threading.Timer):
    def run(self):
        # cancel()が呼ばれるまで繰り返す
        while not self.finished.wait(self.interval):
            self.function(*self.args, **self.kwargs)

# 使い方
def my_task(name):
    print(f"Hello, {name}! Time: {time.ctime()}")

# 2秒おきに my_task("Python") を実行
tm = RepeatingTimer(2.0, my_task, args=("Python",))
tm.start()

time.sleep(7) # 7秒間動かす
tm.cancel()   # タイマー停止
print("タイマーを停止しました。")

6. 知っておきたい!Timerを使う時の注意点

非常に便利な threading.Timer ですが、以下の点には注意が必要です。

  1. 実行時間のズレ(ドリフト):Pythonの Timer は厳密なリアルタイム性を保証するものではありません。OSの負荷や処理時間の影響で、数ミリ秒〜数十ミリ秒程度の遅延が発生することがあります。
  2. スレッドセーフ:メイン処理とタイマー処理で同じ変数を書き換える場合は、データの整合性に注意が必要です。
  3. デーモンスレッド:メインのプログラムが終了してもタイマーが残り続けることがあります。必要に応じて cancel() を確実に呼ぶようにしましょう。

7. まとめ

Pythonの threading.Timer を使えば、メイン処理の邪魔をせずにスマートな周期処理を実装できます。

  • 1回だけ実行: シンプルな Timer(秒, 関数)
  • 周期実行: 関数内での再起動、またはクラス化を推奨
  • 非同期: メインループを止めずに済む

組み込み機器のポーリング処理や、定期的なステータスチェックなど、ぜひ活用してみてください!

コメント

タイトルとURLをコピーしました