Python の Event でスレッド間のシグナルを送る

Event(イベント)は、スレッド間でシンプルなシグナルを送るための同期機構です。フラグの設定とクリアだけでスレッド間の通信ができるため、Condition よりも使いやすい場面があります。

Event の基本

Event は内部にブーリアンフラグを持っています。set() でフラグを立て、clear() でフラグを下ろし、wait() でフラグが立つまで待機します。

import threading
import time

event = threading.Event()

def waiter():
    print("Waiter: イベントを待機中...")
    event.wait()  # フラグが立つまで待機
    print("Waiter: イベント受信!")

def setter():
    time.sleep(2)
    print("Setter: イベントを設定")
    event.set()  # フラグを立てる

t1 = threading.Thread(target=waiter)
t2 = threading.Thread(target=setter)

t1.start()
t2.start()
t1.join()
t2.join()

Event のメソッド

set()

フラグを True にする。待機中のすべてのスレッドが起床する。

clear()

フラグを False にする。

wait(timeout=None)

フラグが True になるまで待機。タイムアウト指定可能。

is_set()

フラグの状態を確認。

wait() のタイムアウト

wait() にタイムアウトを指定すると、フラグが立たなくても指定時間後に戻ります。

import threading

event = threading.Event()

def waiter():
    print("待機開始")
    result = event.wait(timeout=2)  # 最大2秒待つ
    if result:
        print("イベント受信")
    else:
        print("タイムアウト")

thread = threading.Thread(target=waiter)
thread.start()
thread.join()
# タイムアウト(2秒後)

繰り返し使う場合は clear()

一度 set() したイベントを再利用するには、clear() でリセットする必要があります。

import threading
import time

event = threading.Event()

def worker():
    for i in range(3):
        event.wait()  # イベントを待つ
        print(f"ラウンド {i + 1} 実行")
        event.clear()  # 次の待機のためにリセット

def controller():
    for i in range(3):
        time.sleep(1)
        print(f"ラウンド {i + 1} 開始を通知")
        event.set()
        time.sleep(0.1)  # worker が処理する時間を確保

t1 = threading.Thread(target=worker)
t2 = threading.Thread(target=controller)

t1.start()
t2.start()
t1.join()
t2.join()

実用例:スレッドの停止シグナル

Event はスレッドに停止を通知する際によく使われます。

import threading
import time

stop_event = threading.Event()

def worker():
    while not stop_event.is_set():
        print("処理中...")
        time.sleep(0.5)
    print("停止シグナル受信、終了します")

thread = threading.Thread(target=worker)
thread.start()

time.sleep(2)
print("停止シグナルを送信")
stop_event.set()

thread.join()

実用例:初期化完了の待機

メインスレッドがワーカースレッドの初期化完了を待つパターンです。

import threading
import time

initialized = threading.Event()

def worker():
    print("Worker: 初期化中...")
    time.sleep(2)  # 初期化処理
    print("Worker: 初期化完了")
    initialized.set()  # 初期化完了を通知
    
    print("Worker: メイン処理開始")
    time.sleep(1)
    print("Worker: メイン処理完了")

thread = threading.Thread(target=worker)
thread.start()

print("Main: Worker の初期化を待機")
initialized.wait()
print("Main: Worker の初期化完了を確認、処理を続行")

thread.join()

Event と Condition の違い

Event

シンプルなフラグベース。set() ですべての待機スレッドが起床。データの受け渡しには向かない。

Condition

より柔軟。notify() で1つだけ起こすことも可能。複雑な条件での待機に対応。

単純な「準備完了」や「停止」のシグナルには Event、複雑な条件や producer-consumer パターンには Condition を使うのがおすすめです。