セッション・プロセス・スレッド ―― 違い
1️⃣ 序章:なぜ区別が重要か
- 同じ「並列」「接続」といった言葉でも、対象により設計ポイントはまったく異なる。
- リソース最適化・障害切り分け・スケーラビリティ確保のため、3 つの概念を明確に区別することがインフラとアプリ双方の健全性につながる。
2️⃣ 用語の本質
■ セッション Session
- 論理的な利用時間。クライアントとサーバが「いま同じやり取りをしている」という状態を保持する器。
- 中身:認証トークン、ショッピングカート、機械学習モデルの中間キャッシュ……。
- 寿命:ログイン〜ログアウト、接続〜切断。タイムアウトで自動失効させて資源を回収。
■ プロセス Process
- OS が PID と専用メモリ空間を割り当てた 実行単位。
- 特徴:互いに独立したアドレス空間。クラッシュしても他プロセスに波及しにくい。
- 寿命:
fork/exec
・subprocess
で生成→exit()
で終了。
■ スレッド Thread
- 同じプロセス内部で並列実行する 軽量タスク。
- 共有:ヒープ/グローバル変数。分離:スタック、レジスタ。
- 寿命:
pthread_create
・threading.Thread
などで生成→join
で合流。
3️⃣ 具体例:理解を深める 3 シーン
3‑1. 図書館アナロジー
- プロセス=あなた:専用の机と椅子(メモリ空間)。
- スレッド=あなたの両手と目:同じ机で本を読む・メモを書く。
- セッション=入館から退館まで:図書館側が「利用中」と認識している期間。
3‑2. FastAPI + Redis での Web サービス
-
uvicorn --workers 4
→ 4 プロセス。各プロセスが CPU コアを占有。 - 各プロセス内で
asyncio
が多数のコルーチンを捌き、背景処理は スレッドプール でバイナリ変換を実行。 - ユーザはクッキーのセッション ID を送り続け、Redis に保存された状態を共有。
3‑3. GPU 推論 API(SAM2 など)
- 1 GPU=1 プロセスで VRAM を独占し OOM を防止。
- 1 プロセス内で複数リクエストを CUDA Stream に振り分け、スレッドを使わず非同期実行。
- セッションは「同じ動画に対して add_point→propagate を繰り返す」一連の流れ。
4️⃣ 判断フロー:プロセス数/スレッド数はこう決める
-
タスク分類
- CPU バウンド? I/O バウンド? GPU バウンド?
-
律速資源の計測
-
py-spy
,htop
,nvidia-smi
等で CPU%, IO Wait, GPU Util を観測。
-
-
並列化単位の選択
- Python + GIL → CPU バウンドはプロセス、I/O バウンドはスレッド/async。
- C/C++や Rust → スレッドでコア数まで水平展開。
-
負荷試験で飽和点を探す
- Locust や hey で RPS を徐々に増やし頭打ち直前がベストプラクティスの目安。
📐 目安値
ワークロード | 推奨プロセス数 | 推奨スレッド数 |
---|---|---|
CPU バウンド (Python) | 物理コア数 | 1 (又は無効) |
I/O バウンド API | 2×コア+1 | 100〜∞ (I/O 量で調整) |
GPU 大型モデル | 1 / GPU | 0〜1 (Stream で非同期) |
DataLoader | コア/2 | 自動(PyTorch が fork) |
5️⃣ 実装 Tips & アンチパターン
- スレッド乱立:文脈切替が増え逆に遅くなる。I/O 待ち比率 <50% ならスレッド削減を検討。
- プロセス過剰:メモリ枯渇+ロード時間増大。モデル 2GB×8 プロセス→起動時に 16GB 消費。
- セッション漏れ:期限切れトークンを掃除しないと Redis が膨張、レイテンシ悪化。
-
GIL 過信:Python C 拡張内部で GIL 解放されるか要確認。NumPy 演算は解放済み、
random
生成は解放なし。
6️⃣ まとめ
- セッションはユーザ視点、プロセスは OS 視点、スレッドはプログラム実装視点での並列概念。
- 3 つを適材適所に組み合わせれば「安全性・性能・保守性」を両立。
- 決め打ちではなく 計測 → 仮説 → 負荷試験 → 再計測 のループが最速最適化ルート。