⚔️スレッドと非同期処理の違いを理解する
🟢 はじめに
Pythonでは「複数の処理を並行して進める」ための手段として、
-
threading
を使った スレッド処理 -
asyncio
を使った 非同期処理(async/await)
の2つが存在する。
このノートでは、「どちらも並行して処理できるのに、何が違うのか? どちらをいつ使うべきか?」という点を直感的かつ具体的に整理する。
🧵threading:物理的に“分身”して動くイメージ
✅ 特徴
-
スレッドごとに実行の流れ(スタック)を持つ。
-
複数のスレッドが同時に本当に動いているように見える。
-
CPUバウンド/IOバウンド問わず使えるが、GILの影響でCPUバウンドには弱い(後述)。
🧠たとえ:何人もの作業員を雇う
-
シェフが何人もいて、同時に異なる作業を進められる。
-
ただし、厨房が狭い(GIL)ので、本当に同時には動けない瞬間もある。
⏳asyncio:1人が効率よく“切り替えながら”動くイメージ
✅ 特徴
-
実際には1つのスレッドで処理をしており、待ち時間(I/Oなど)をうまく活用して切り替えて動いている。
-
処理の中に
await
を入れることで、「ここは他の処理に切り替えてOK」という中断点を明示する。 -
IOバウンド処理に特化。CPUバウンドな重い処理には向いていない。
🧠たとえ:1人のマルチタスク職人
-
シェフは1人だけだが、
-
オーブンで焼き始めたらすぐ冷蔵庫の準備に移り、
-
そのあと注文票を読みつつ、次の工程へ。
-
-
待っている時間に別の作業を差し込むことで、1人でも効率がよい。
🆚 スレッド vs 非同期処理:比較表
比較項目 | threading (スレッド) |
asyncio (非同期) |
---|---|---|
並行の仕組み | 分身(複数スレッド) | 1人で切り替え(イベントループ) |
並行実行できるか? | ✅ 実際に複数スレッドで動く | ✅ ただし「同時」ではなく「切り替え」 |
I/O待ちへの強さ | ✅ 強い | ✅ 非常に強い(本領発揮) |
重たい計算処理 | ❌ GILの制限により真の並列にならない(複数プロセスが必要) | ❌ 1スレッドなので向いていない |
コードの書きやすさ | シンプルだが同期・排他制御が必要 | 慣れが必要だが設計が綺麗(状態管理しやすい) |
UIとの親和性 | △ GUIで使うときは注意が必要(メインスレッド制御) | ❌ GUIとは直接相性が良くない |
適している場面 | マルチスレッドで定期実行・UI処理・並列クローリングなど | 非同期HTTP、ファイルI/O、リアクティブWebなど |
🧠GILとは?
Python(CPython)には「GIL(Global Interpreter Lock)」があり、
-
同時に複数スレッドが純粋なPythonコードを実行できないという制限がある。
-
つまり
threading
では、スレッドが複数あっても、CPUバウンド処理(計算など)は順番にしか実行されない。
C言語などのネイティブコードやI/O操作中はGILを解放するため、threading
でもI/Oバウンド処理には効果がある。
🧪例で理解する
🔁 スレッドで複数の処理を同時に走らせる
import threading
import time
def task(name):
time.sleep(1)
print(f"{name} 終了")
for i in range(3):
threading.Thread(target=task, args=(f"タスク{i}",)).start()
→ 同時に3つの処理が並行で進む。
🌀 非同期で並行に処理する
import asyncio
async def task(name):
await asyncio.sleep(1)
print(f"{name} 終了")
async def main():
await asyncio.gather(
task("タスク0"),
task("タスク1"),
task("タスク2")
)
asyncio.run(main())
→ 実行中に await
があることで、他のタスクに切り替わりながら進む。
🎯使い分けの判断ポイント
状況 | おすすめ手段 | 理由 |
---|---|---|
HTTPアクセスを多数同時にしたい | asyncio |
非同期にリクエストし、待ち時間を有効活用できる |
GUIアプリで定期処理をバックグラウンド実行したい | threading |
非同期だとUIが止まる。別スレッドで動かすのが王道 |
CPUを食う重たい処理を複数並行で実行したい | multiprocessing |
スレッドも非同期もGILの影響を受ける。別プロセスに分けるべき |
📝まとめ
-
**スレッド(threading)**は「分身して動く」感覚。複雑な排他制御は必要だが、並列処理が可能。
-
**非同期(asyncio)**は「1人の職人が効率よく切り替える」スタイル。設計が綺麗で、I/Oに最適。
-
両者は見た目が似ていても、中身の動作は全く違う。
-
GILの存在を意識して、「どんな処理を並行させたいのか?」で選ぶのがカギ。