並行処理
複数のタスクを論理的に同時に進行させるプログラミング手法で、システムのスループットと応答性を向上させる
並行処理パフォーマンス
並行処理とは
並行処理 (Concurrency) は、複数のタスクを論理的に同時に進行させるプログラミング手法である。並列処理 (Parallelism) とは異なり、物理的に同時に実行する必要はない。シングルコアでもタスクを切り替えながら並行処理できる。
並行 (Concurrency) vs 並列 (Parallelism)
| 概念 | 説明 | 例 |
|---|---|---|
| 並行 | 複数タスクを論理的に同時進行 | 1 人のシェフが複数の料理を交互に調理 |
| 並列 | 複数タスクを物理的に同時実行 | 複数のシェフが同時に調理 |
並行 (1 コア): [Task A][Task B][Task A][Task B] ← 切り替えながら進行
並列 (2 コア): [Task A][Task A] ← 同時に実行
[Task B][Task B]
言語ごとの並行処理モデル
| 言語 | モデル | 特徴 |
|---|---|---|
| Node.js | イベントループ + async/await | シングルスレッド、I/O に最適 |
| Go | goroutine + channel | 軽量スレッド、数百万の goroutine |
| Rust | async/await + tokio | ゼロコスト抽象化、メモリ安全 |
| Java | スレッド + Virtual Threads (21+) | OS スレッド or 軽量スレッド |
Node.js の並行処理
Node.js はシングルスレッドのイベントループで I/O を並行処理する。
// ❌ 直列: 各 await が前の完了を待つ (300ms)
const user = await getUser(id); // 100ms
const orders = await getOrders(id); // 100ms
const config = await getConfig(); // 100ms
// ✅ 並行: 独立した処理を同時実行 (100ms)
const [user, orders, config] = await Promise.all([
getUser(id),
getOrders(id),
getConfig(),
]);
CPU バウンド vs I/O バウンド
| ワークロード | ボトルネック | Node.js での対策 |
|---|---|---|
| I/O バウンド | ネットワーク、ディスク | async/await (イベントループ) |
| CPU バウンド | 計算処理 | Worker Threads |
Node.js のイベントループは I/O バウンドな処理に最適だが、CPU バウンドな処理 (画像処理、暗号化) はイベントループをブロックする。Worker Threads で別スレッドに委譲する。
Lambda での並行処理
Lambda は関数の同時実行で並行処理を実現する。1 つのリクエストに対して 1 つの Lambda インスタンスが起動し、複数リクエストは複数インスタンスで並行処理される。
リクエスト 1 → Lambda インスタンス A
リクエスト 2 → Lambda インスタンス B ← 同時実行
リクエスト 3 → Lambda インスタンス C
よくある問題
レースコンディション
共有リソースへの同時アクセスで、実行順序によって結果が変わるバグ。DynamoDB の条件付き書き込みやアトミックカウンターで防ぐ。
デッドロック
2 つのタスクが互いのロック解放を待ち、永遠にブロックされる状態。ロックの取得順序を統一することで防ぐ。
並行処理については関連書籍でも詳しく扱われている。