イベントループとは?
イベントループは、非同期処理を管理する仕組みで、タスク(イベント)を順番に処理するためのループです。名前の通り、イベント(非同期タスク)がキューに追加され、それをループしながら処理していく様子を表しています。
Pythonでは、asyncio
モジュールを使って非同期処理を実現します。このモジュールの中心的な役割を果たすのがイベントループです。
イベントループの動作イメージ
イベントループは以下のように動作します:
-
タスクの登録: 非同期関数(
async def
で定義された関数)の戻り値であるコルーチンオブジェクトがイベントループにタスクとして登録されます。登録されたタスクはタスクキューに追加されます。 - タスクの実行: イベントループはタスクキューからタスクを1つずつ取り出して実行します。
- 待機状態のタスク: タスクがawaitで待機状態になると、イベントループはそのタスクを一時停止し、他のタスクを実行します。
- タスクの再開: 待機状態が解除されると、タスクは再びタスクキューに戻され、実行が再開されます。
キューの役割
イベントループが内部で使用するキュー(FIFO)には以下のような役割があります。
-
タスクキュー:
- 実行待ちの非同期タスクを処理します
- タスクが完了するか、待機状態になるまで順番に実行されます。
-
イベントキュー:
- I/O操作やタイマーイベントなど、外部から発生したイベントを管理します。
- イベントが発生すると、対応するコールバックがタスクキューに追加されます。
1. タスクキュー(Task Queue)
- 主な役割: 非同期で実行すべき「タスク」(関数やコールバック)を順番に管理するキューです。
-
例: Pythonの
asyncio
ではawait
で待機しているコルーチンなどがタスクキューに入ります。 - イベントループの動作: イベントループはタスクキューからタスクを1つずつ取り出して実行します。
2. イベントキュー(Event Queue)
- 主な役割: 外部から発生した「イベント」(I/O完了通知、ユーザー操作、ネットワーク受信など)を管理するキューです。
- 例: ファイルの読み込み完了、ソケット通信の受信、ユーザーのクリックイベントなど。
- イベントループの動作: イベントループはイベントキューからイベントを取り出し、それに対応するコールバックやタスクをタスクキューに追加します。
Python(asyncio)の場合
- タスクキュー: 実行待ちのコルーチンやコールバックが入る。
-
イベントキュー: OSやライブラリが通知するI/Oイベントなどが入る(内部的には
selector
などで管理)。 - asyncioのイベントループは、I/Oイベントを検知すると対応するコールバックをタスクキューに追加し、順次実行します。
Pythonでのイベントループの例
以下はPythonでイベントループを使った非同期処理の例です:
import asyncio
async def task1():
print("Task 1: 開始")
await asyncio.sleep(2) # 非同期で2秒待機
print("Task 1: 終了")
async def task2():
print("Task 2: 開始")
await asyncio.sleep(1) # 非同期で1秒待機
print("Task 2: 終了")
async def main():
# タスクをイベントループに登録
await asyncio.gather(task1(), task2())
# イベントループを開始
asyncio.run(main())
実行結果
Task 1: 開始
Task 2: 開始
Task 2: 終了
Task 1: 終了
動作の流れ
- task1, task2がイベントループに登録され、タスクキューに追加されます。
- イベントループがタスクキューからタスクを取り出して実行します。
- await asyncio.sleep() によってタスクが待機状態になると[待機中のタスクリスト]にタスクが登録され、他のタスクが実行されます。
- await待機が終了すると[待機中のタスクリスト]からタスクが再びタスクキューに戻され、実行が再開されます。
イベントループの特徴
- イベントループは1つのスレッドで動作します(シングルスレッド)。
- 非同期処理を効率的に管理し、I/O待機中でも他のタスクを実行できます。
イベントループは「イベントがループしている様子」を表していると言えますが、実際には「登録された非同期タスクを順番に処理する仕組み」を指します。
Q. ループしていないのになぜイベントループという名称がつけられているのですか?
A. イベントループという名称がつけられている理由は、イベントを処理する仕組みが「ループのように繰り返し動作する」 からです。実際には、イベントループは内部的に繰り返し処理を行っているため、「ループしていない」というわけではありません。
イベントループの動作
イベントループは以下のような流れで動作します:
- イベントキューを監視: イベントループは、非同期タスクやI/O操作の完了通知などが登録される「イベントキュー」を監視します。
- イベントの取り出し: キューにイベントがある場合、それを取り出して処理します。
- 処理の繰り返し: キューが空になるまで、またはプログラムが終了するまで、この処理を繰り返します。
この「イベントキューを監視し、イベントを処理する」という動作が、ループのように繰り返されるため、「イベントループ」と呼ばれています。
実際のループの例
イベントループは内部的には、以下のような仕組みで動作しています(擬似コード):
while True: # 無限ループ
event = get_next_event() # キューから次のイベントを取得
if event:
process_event(event) # イベントを処理
else:
wait_for_event() # イベントが来るまで待機
このように、イベントループは実際に「ループしている」仕組みを持っています。
まとめ
イベントループという名称は、内部的にイベントを処理する仕組みが「ループのように繰り返し動作する」ことに由来しています。開発者が直接ループを記述しない場合でも、イベントループは裏で繰り返し処理を行っています。