並行処理と非同期

並行処理とは

サーバーの処理時間の多くは、実は「待ち時間」です。データベースの応答、外部APIの返事、ファイルの読み書き — その間 CPU は何もしていません。この待ち時間に別の仕事を進めるのが並行処理です。1人の店員が、麺をゆでている間にレジを打つイメージです。

並行と並列の違い

並行(Concurrency)並列(Parallelism)
意味複数の処理を切り替えながら進める複数の処理を同時に実行する
必要なもの切り替えの仕組み複数のCPUコア
例え1人の店員が複数の作業を回す店員が2人いて同時に働く

CPUコアが1つでも並行処理はできますが、並列実行はできません。待ち時間の性質でも手段は変わり、通信やディスクの待ちが中心のI/Oバウンドには非同期I/Oやイベントループが、計算そのものが重いCPUバウンドにはコア数を活かした並列実行が向きます。Webアプリの多くはI/Oバウンドで、「待っている間に次のリクエストを受ける」だけで大きく性能が上がります。

実現方法の比較

方式特徴代表例
マルチプロセスメモリが独立して安全だが重い[[プロセスとスレッド]] のプロセス分離
マルチスレッド軽いがメモリ共有による事故が起きやすいJava、C#
イベントループ1スレッドで待ちを回す。I/Oに強い[[Node.js]]
軽量スレッド言語ランタイムが数万単位で管理する[[Go]] のゴルーチン

非同期プログラミング

非同期は「結果を待たずに次へ進み、できたら受け取る」書き方です。JavaScript の async/await が代表例で、互いに依存しない処理を直列に await するのは初心者がやりがちな性能低下パターンです。

const a = await fetchUser();   // 1秒
const b = await fetchOrders(); // 2秒 → 直列なので合計3秒
const [a2, b2] = await Promise.all([fetchUser(), fetchOrders()]); // 同時なら2秒

よくある落とし穴

  • 競合状態: 複数の処理が同じデータを同時に書き換え、結果が実行順に左右される
  • デッドロック: 互いに相手のロック解放を待ち続け、永久に止まる
  • イベントループのブロック: 1スレッド方式で重い計算をすると、全リクエストが止まる。時間のかかる処理は [[メッセージキュー]] に投げて別プロセスに任せるのが定石

初学者向けポイント

  • まず「この遅さは待ちか、計算か」を切り分ける。手段の選択はそこから
  • 共有データを減らすのが最良の対策。共有しなければ競合も起きない
  • 並行にすると処理の順序は保証されない。順序が必要なら明示的に制御する

関連技術とのつながり

  • [[プロセスとスレッド]] — 並行処理を支えるOSレベルの単位
  • [[Node.js]] — イベントループによる非同期I/Oの代表例
  • [[Go]] — 軽量スレッドで並行処理を書きやすくした言語
  • [[メッセージキュー]] — 重い処理を切り離して非同期に実行する仕組み
  • [[ブラウザのマルチプロセスアーキテクチャ]] — ブラウザ側のJSも1本のメインスレッドで動くため、重い処理でそこを塞ぐと画面が固まる。イベントループを塞ぐ問題がクライアント側に現れた形
Q: 並行(Concurrency)と並列(Parallelism)の違いとして正しいのはどれ?
- [x] 並行は切り替えながら進めること、並列は複数を同時に実行すること
- [ ] 並行はCPUを2個以上使い、並列は1個で足りる
- [ ] 両者は完全に同じ意味である
解説: CPUコアが1つでも並行処理は可能ですが、本当の同時実行である並列には複数コアが必要です。

Q: 非同期I/Oの効果が特に大きいのはどのような処理?
- [ ] 大量の数値計算が中心のCPUバウンドな処理
- [x] DBや外部APIの応答待ちが中心のI/Oバウンドな処理
- [ ] メモリを大量に使う処理
解説: 待ち時間に別の仕事を進められるため、I/Oバウンドな処理で効果が大きく出ます。

Q: 1スレッドのイベントループ方式で避けるべきことはどれ?
- [ ] 複数のAPI呼び出しを同時に走らせる
- [x] 重い計算処理をその場で実行してループを塞ぐ
- [ ] 結果を待たずに次の処理へ進む
解説: 1スレッドを占有すると全リクエストが止まります。重い処理はキュー経由で別プロセスに任せます。