Python threading.Timerとは?指定秒後に処理を実行する方法

threading.Timerは、指定した秒数が経ってから関数を実行するクラスです。

time.sleep()と違い、待っている間もプログラムは先に進みます。


Timerの基本

import threading
import time

def hello():
    print("3秒後に実行")

t = threading.Timer(3, hello)
t.start()

print("Timer開始(まだ実行されない)")
time.sleep (4)

実行結果:

Timer開始(まだ実行されない)
3秒後に実行

第1引数が秒数、第2引数が実行する関数です。

start()を呼ばないと動きません。作っただけでは何も起きない点に注意してください。


引数を渡す

def hello(name, greeting="こんにちは"):
    print(f"{greeting}、{name}さん")

threading.Timer(0.1, hello,
                args=("田中",),
                kwargs={"greeting": "やあ"}).start()

実行結果:

やあ、田中さん

Threadと同じくargsはタプルです。引数が1個なら("田中",)とカンマを付けてください。


cancel()で取り消す

t = threading.Timer(5, hello)
t.start()
t.cancel()

print("cancel直後 is_alive:", t.is_alive())
time.sleep (0.1)
print("少し待つと:", t.is_alive())

実行結果:

cancel直後 is_alive: True
少し待つと: False

cancel()は「取り消しを依頼する」だけで、スレッドが終わるまでにわずかな時間差があります。

確実に終わったことを確認したいなら、cancel()のあとにjoin()を呼んでください。

すでに実行されたあとのcancel()は何も起きません——エラーにもなりません。


Timerはスレッドである

t = threading.Timer(1, hello)
print(isinstance(t, threading.Thread))

実行結果:

True

TimerはThreadのサブクラスです。つまり1つのTimerにつき1つのスレッドが作られます。

ここから2つの注意点が出てきます。

  • 大量に作るとスレッドが増えすぎる——数千個のタイマーには向かない
  • デフォルトでは非デーモンなので、実行を待ってからプログラムが終わる
t = threading.Timer(10, hello)
t.daemon = True      # start前に設定
t.start()

daemon = Trueにすれば、メイン終了時に道連れで終わります。「Pythonのデーモンスレッドとは?daemon=Trueの意味と注意点」を参照してください。


繰り返し実行する

Timerは1回しか実行しません。繰り返したいときは、自分の中で作り直します。

import threading

count = 0

def tick():
    global count
    count += 1
    print("tick", count)
    if count < 3:
        threading.Timer(0.1, tick).start()

tick()

実行結果:

tick 1
tick 2
tick 3

終了条件を必ず入れてください。条件を書き忘れると、止める手段がないまま永久に動き続けます。

停止できるようにするなら、Eventと組み合わせるか、作ったTimerを変数に保持してcancel()できるようにします。


精度は保証されない

import time

s = time.perf_counter()
ev = threading.Event()
threading.Timer(0.5, ev.set).start()
ev.wait()

print(f"指定0.5秒 → 実測{time.perf_counter() - s:.3f}秒")

実行結果:

指定0.5秒 → 実測0.505秒

「指定秒数以上待つ」ことは保証されますが、ぴったりではありません。

スレッドの起動や他の処理の影響で、必ず少し遅れます。ミリ秒単位の正確さが要る用途には使えません。


sleepとの使い分け

time.sleep() Timer
呼び出し側 止まる 止まらない
取り消し できない cancel()
スレッド 増えない 1つ増える

順番に待てばよいだけならtime.sleep()で十分です。

待ちながら他の処理を続けたい、あとで取り消したい——そのときがTimerの出番です。


Timerのまとめ

  • Timer(秒数, 関数)を作ってstart()で開始
  • argsはタプル(引数1個ならカンマ必須)
  • cancel()で取り消せる——終了確認はjoin()
  • 1つにつきスレッド1本——大量生成は避ける
  • 実行は1回だけ——繰り返すなら作り直す
  • 精度は保証されない(指定秒数以上)

あわせて読みたい

プログラミング学習に興味がある方は、まずは無料体験会へ

無料体験会・お申し込みはこちら