【Python】裏側で処理を動かす!threading.Timerを使った定期実行(周期処理)の基本と応用

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

Pythonでプログラムを作っていると、「メインの処理を動かしながら、裏で5秒おきにセンサーの値を読み取りたい」「1分ごとにログを出力したい」といった定期実行(周期処理)が必要になることがよくあります。

特に通信アプリや組み込み系の制御ソフトでは必須のテクニックですが、これをPythonで手軽かつ強力に実現できるのが、標準ライブラリのthreading.Timerです。

この記事では、初心者向けにthreading.Timerの基本から、実戦で使える「周期的な実行方法」「クラス化による管理」まで分かりやすく解説します。

定期実行を実現する手法の比較

Pythonで処理を繰り返す方法はいくつかありますが、用途によって向き不向きがあります。

  • while + sleep:一番簡単ですが、待機している間はメインの処理が完全にストップしてしまいます。
  • scheduleライブラリ:直感的に書けますが、外部ライブラリをインストールする手間がかかります。
  • threading.Timer(おすすめ!):標準機能なのでインストール不要。「スレッド」を使うため、待機中もメイン処理を止めずに裏側で動かせます。

threading.Timerとは?

threading.Timerは、Pythonに標準搭載されているthreadingモジュールの一部です。

「指定した時間が経過した後に、特定の関数を別のスレッドで実行する」という役割を持っています。メインループを邪魔せずに非同期で動いてくれるため、定期実行に最適です。

【基本】まずは「◯秒後に1回だけ」実行してみる

最初は最もシンプルな基本形です。

import threading

def hello():
    print("3秒経過しました!(裏側で実行されました)")

# 1. タイマーの準備: Timer(待機する秒数, 実行する関数)
t = threading.Timer(3.0, hello)

# 2. タイマーのスタート
t.start()

print("タイマー開始!この行は待たずにすぐ表示されます。")

このコードを実行すると、最後のprint文がすぐに表示され、その3秒後にhello関数が実行されます。メインの処理が止まっていないことが分かりますね。

【実践】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:
    # Ctrl+Cなどが押されたらタイマーを停止して終了
    print("停止中...")
    timer.cancel() 

【応用】実戦向け!クラス化して使いやすくする

実際のアプリ開発では、先ほどの例のようにglobal変数を使うのは管理が煩雑になります。そこでおすすめなのが、タイマーをクラス化して再利用性を高める方法です。

以下のコードはコピペしてそのまま「周期タイマークラス」として使い回せます。

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()

# メイン処理を7秒間動かす
time.sleep(7) 

# タイマーを安全に停止
tm.cancel() 
print("タイマーを停止しました。")

クラス化しておくことで、複数のタイマーを動かしたり、任意のタイミングで止めたりするのが非常に簡単になります。

Timerを使う時の注意点

便利なthreading.Timerですが、実戦で使う際は以下の点に気をつけましょう。

  1. 時間のズレ(ドリフト):Pythonのタイマーは、厳密なリアルタイム性を保証するものではありません。PCの負荷などにより数ミリ〜数十ミリ秒の遅延が起こり得ます。
  2. スレッドセーフ:メイン処理とタイマー処理の両方で「同じ変数」を書き換えるような場合は、データがおかしくならないよう注意(ロック処理など)が必要です。
  3. 確実な停止:プログラムを終了させる際は、必ずcancel()を呼んでタイマーを止めましょう。裏で動き続けてしまうことがあります。

まとめ

この記事では、Pythonのthreading.Timerを使った定期実行(周期処理)について解説しました。

  • 単発実行は Timer(秒数, 関数) で簡単に実装できる。
  • 周期実行させたい場合は、関数内での「再起動」か「クラス化」を活用する。
  • メイン処理を止めずに(非同期で)裏側で動かせるため非常に便利。

組み込み機器のセンサー確認(ポーリング処理)や、定期的なバックアップ処理、ステータスチェックなど、さまざまな場面で活躍するテクニックなので、ぜひ活用してみてください!

コメント

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