並行プログラミング
🧩 並行プログラミングのモデル体系
💾 メモリ共有方式
スレッドが共有メモリを介して協調するか、メッセージ通信で協調するか
🧠 制御構造
明示的制御(スレッド・ロック)か、抽象化(タスク・アクタ・データフロー)か
⚙️ 同期方式
同期的通信か、非同期的通信か
📐 抽象レベル
OSレベル(プロセス・スレッド)か、言語レベル(アクタ・チャネル・Futureなど)か
実世界ではモデルは「混在」する
実際のシステムでは、複数モデルを組み合わせることが多いです。
例:
Go → CSP + Shared Memory(必要ならsync.Mutex)
Akka → Actor + Future + Stream
Rust → Message Passing + Future + async/await
Node.js → Event Loop + Promise + Stream
並行プログラミングのモデル
共有メモリモデル (Shared Memory Model)
複数のスレッドが同じメモリ空間を共有し、変数やデータ構造を直接読み書きして協調します
特徴
メモリアクセスが高速(コピー不要)
だが競合状態 (Race Condition) が発生しやすい
同期機構(Lock, Mutex, Monitorなど)が必須
代表的手法
ミューテックス (Mutex)
排他ロックによって共有資源への同時アクセスを防ぐ
セマフォ (Semaphore)
限定数のスレッドを同時実行可能にする
条件変数 (Condition Variable)
特定の条件が満たされるまで待機する
モニタ (Monitor)
ロックと条件変数を抽象化して自動管理する(例:Java synchronized)
メッセージパッシングモデル (Message Passing Model)
スレッドやプロセスは独立したメモリ空間を持ち、データを共有せず、メッセージ通信によって連携します。
特徴
共有状態がないため競合が原理的に発生しにくい
通信コストがやや高い
構造が疎結合で拡張しやすい
代表的モデル
アクターモデル (Actor Model)
各「アクター」が独立した状態と受信キューを持ち、非同期メッセージで通信。
アクターは「メッセージを受信 → 状態を更新 → 新しいメッセージを送信」だけで動作。
共有メモリ不要。スケールしやすい。
例
Erlang / Elixir
Akka(Scala / Java)
CSPモデル (Communicating Sequential Processes)
「チャネル (Channel)」を通じてプロセス間で通信。
Go言語の goroutine + channel はこの思想を継承。
通信が同期的(片方が送受信待ち)であることが特徴。
例
Go (goroutine)
Rustの crossbeam-channel
Occam(CSPの元祖)
Fork/Joinモデル (Fork/Join Model)
タスクを再帰的に分割(fork)し、並列に実行して結果を合流(join)するモデル。
分割統治アルゴリズムを並列化するための仕組みとして設計されています。
ソート、行列演算、木探索、画像処理、科学計算などCPUバウンド処理に最適
代表的実装
Java ForkJoinPool
C/C++ OpenMP (#pragma omp task)
Rust Rayon (par_iter())
Scala scala.concurrent.ForkJoinTask
データフローモデル (Dataflow Model)
処理はデータ依存関係によって駆動される。
データが揃った瞬間にノード(演算)が発火して実行される。
特徴
「どの順に実行するか」ではなく「どのデータが依存しているか」を表現。
並列実行しやすいが、制御構造の表現が難しい。
代表例
TensorFlow / PyTorch の計算グラフ
ReactiveX(RxJava, RxJS, etc.)
Haskellの「遅延評価」も一種のデータフロー的性質を持つ
Future / Promiseモデル
「将来完了する値(Future)」をオブジェクトとして扱う。
非同期タスクの結果を安全に参照する抽象。
特徴
コールバック地獄を避けやすい
値の依存関係を明示できる
多くの言語で採用(JavaScript, Scala, Java, Pythonなど)
代表例
JavaScript Promise, async/await
Scala Future
Python asyncio, Future
Java CompletableFuture
リアクティブモデル (Reactive / Event-driven Model)
外部からのイベント(入力・通知)に応じて非同期に反応するモデル。
「イベントループ」が中心にあり、I/O待ちを効率化。
特徴
シングルスレッドでも高スループット(ノンブロッキングI/O)
状態管理が複雑になりやすい
代表例
Node.js Event Loop
Reactive Streams, RxJava, Project Reactor
Akka Streams
トランザクショナルメモリモデル (Transactional Memory)
メモリ操作をトランザクションとして扱い、衝突があれば自動で再試行。
データベースのACID的概念をメモリ操作に拡張したもの。
特徴
ロックを明示的に使わずに並行性を確保
実装コストが高く、まだ広くは普及していない
代表例
Haskell STM (Software Transactional Memory)
Clojure ref + dosync
Intel TSX (Hardware Transactional Memory)
ストリームモデル (Stream / Pipeline Model)
データの流れをパイプライン化し、各ステージを並行に処理する。
特徴
処理の構造化に優れる
データ処理(ETL, 分散処理)でよく使われる
代表例
Apache Beam / Flink / Kafka Streams
Unix pipeline (cat file | grep | sort)
Reactive Streams
ディクレラティブ並行モデル (Declarative / Logic-based)
「何をしたいか」を宣言的に記述し、評価順序や並行性は実行系に任せる。
代表例
Prolog(論理プログラミング)
Haskellの純粋関数的並行性 (par, pseq)
Datalog, DataParallel Haskell