TL

並行性モデル(CSP・アクター・STM)

ロックで悩む並行処理を、より安全な計算モデルで設計し直すための地図がここにある。共有メモリ・CSP・アクター・STMの同期原理と向き不向きを整理。

応用並行処理CSPアクターモデルSTM同期最終更新: 2026-06-21
TL;DR要点だけ先に
  • 1.並行性モデルは「状態をどう共有し、どう同期するか」で分類できる。共有メモリ+ロック/メッセージ受け渡し(CSP・アクター)/トランザクション(STM)が四大潮流。
  • 2.CSPはチャネル(通信路)が主役で送受信が同期点、アクターは各アクターが私有状態を持ちメールボックスで非同期受信。両者とも共有を避けてデータ競合を原理的に消す。
  • 3.STMはメモリ操作を楽観的トランザクションで包み、衝突時に自動リトライ。デッドロックは起きないが副作用やライブロックに注意が要る。

なぜ「モデル」で考えるのか

並行処理の難しさの本質は、複数の実行主体が同じ状態に同時に触れることにあります。スレッドが共有変数を読み書きすれば、読み出しと書き込みの間に別スレッドが割り込み、結果が実行順序に依存するデータ競合が生じます。これをロックで守るのが伝統的な手法ですが、ロックはデッドロック・優先度逆転・粒度設計の難しさを抱えます。

そこで「状態をどう共有し、どこで同期を取るか」という計算モデルそのものを選び直す発想が生まれました。本記事では四つの潮流を、同期原理(どの操作が待ち合わせ点になるか)と適用領域で整理します。

3つの軸で分類する

並行性モデルは概ね次の軸で見分けられます。(1) 状態は共有私有か。(2) 同期はロック通信(メッセージ)トランザクションか。(3) メッセージ授受は同期的非同期的か。CSP・アクター・STM はいずれも「生のロック」を避ける方向の答えです。

系統と分岐(年代で追う)

理論の派生関係を時系列で押さえると、各モデルの狙いが見えます。

  • 1965年ごろ:Dijkstra がセマフォを定式化(共有メモリ+ロックの基礎)。
  • 1973年:Hewitt らがアクターモデルを提唱。非同期・私有状態の世界観。
  • 1978年:Hoare が**CSP(Communicating Sequential Processes)**を発表。チャネルでの同期通信で協調。後の Occam・Go の土台。
  • 1986年:Ericsson がErlangでアクターを実用化(軽量プロセス+「let it crash」)。
  • 1995年〜2007年:Shavit らが**STM(Software Transactional Memory)**を提案し、Haskell が型で副作用を隔離する実用形を確立。
年代モデル中心概念代表言語
1973アクター私有状態+メールボックスErlang / Elixir / Akka
1978CSPチャネルでの同期通信Occam / Go
1995STMメモリのトランザクションHaskell / Clojure

共有メモリとロック:すべての出発点

共有メモリモデルでは、複数スレッドが同一アドレス空間を共有し、ミューテックスで臨界区間を直列化します。同期原理は「ロックを保持している間は他者を待たせる」という排他です。

lock(m)
  balance = balance - 100   // 臨界区間:一度に1スレッドだけ
unlock(m)

効率は高い一方、正しさの責任が完全にプログラマ側にあります。複数ロックの取得順序が揃わないとデッドロックが起き、ロック範囲が広すぎれば並行度が落ち、狭すぎれば守り漏れます。以下の三モデルは、この「共有+ロック」の難しさを別の原理で置き換える試みです。

CSP:チャネルが主役、通信が同期点

CSP では実行主体(ゴルーチン等)は状態を共有せずチャネルという通信路を介してのみ値を受け渡します。標語は「メモリを共有して通信するな、通信してメモリを共有せよ」。

同期原理は**ランデブー(待ち合わせ)**です。容量0(unbuffered)のチャネルでは、送信は受信側が受け取るまでブロックし、受信は送信が来るまでブロックします。つまり送受信の一致点が同期点になり、ここでメモリの引き渡しと順序保証が同時に成立します。

ch := make(chan int)        // 容量0:送受信が出会う点で同期
go func() { ch <- compute() }() // 受信者が現れるまで待つ
result := <-ch              // 送信者が現れるまで待つ
バッファの有無で意味が変わる

バッファ付きチャネル(容量N)は、満杯になるまで送信がブロックしない非同期的な振る舞いになり、生産者・消費者の速度差を吸収します。容量0なら厳密なランデブー。「同期点をどこに置くか」を容量で設計するのが CSP 実装の勘所です。

チャネル自体は共有資源なので競合し得ますが、値の所有権を送信時に手放す規律を守れば、同じデータに二者が同時に触れる状況を避けられます。select による多重待ち受けで、複数チャネルのどれかが準備でき次第処理する分岐も書けます。

アクター:私有状態と非同期メールボックス

アクターモデルでは、各アクターが(1) 自分だけがアクセスできる私有状態、(2) 受信箱(メールボックス)、(3) メッセージ処理関数を持ちます。アクターにできることは三つだけ——メッセージを送る、新しいアクターを作る、次に受け取るメッセージへの振る舞いを決める。

CSP との決定的な違いは、通信が非同期な点です。送信はメールボックスへ投函した時点で完了し、受信側の処理を待ちません。各アクターはメッセージを一度に1通ずつ逐次処理するため、私有状態にロックは不要です。状態を変えられるのは自分自身だけだからです。

receive
  {deposit, Amount} -> Balance = Balance + Amount
  {withdraw, Amount} -> Balance = Balance - Amount
  {query, From}      -> From ! Balance
end
観点CSPアクター
通信の媒体チャネル(第三者)宛先アクターのメールボックス
同期/非同期同期(ランデブー)が基本非同期(投函即完了)
宛先の指定チャネルを共有する者同士アクターの識別子(アドレス)
状態共有しない・受け渡すアクターが私有
代表実装Go のゴルーチン+チャネルErlang / Akka
メールボックスは無限ではない

非同期ゆえに、消費が生産に追いつかないとメールボックスが膨張し、メモリを圧迫します(バックプレッシャ不足)。送信側を意図的に待たせる仕組みや上限の設計が要ります。また配送保証は実装依存で、分散環境では「最大1回」「最低1回」など到達保証のセマンティクスを明示する必要があります。

Erlang の「let it crash」は、アクターが独立しているからこそ成立します。あるアクターが異常終了しても他へ状態は波及せず、監督(supervisor)アクターが再起動で復旧します。詳しい例外伝播の考え方は例外処理(エラーハンドリング)も参照してください。

STM:メモリ操作をトランザクションで包む

STM は、複数のメモリ操作を一つのトランザクションとしてまとめ、データベースの ACID にならってアトミックに実行します。同期原理は楽観的並行制御です。実行中は他者をロックせず、コミット時に「読んだ値が途中で変わっていないか」を検証し、衝突していればトランザクション全体を破棄して自動リトライします。

atomically:
  x = readTVar a
  writeTVar a (x - 100)
  y = readTVar b
  writeTVar b (y + 100)   -- a と b の更新は不可分。途中状態は外から見えない

利点は合成可能性です。ロックは「ロックAを取る関数」と「ロックBを取る関数」を組み合わせると順序起因のデッドロックを生みますが、STM のトランザクションは入れ子にしても一つの大きなトランザクションに合流するだけで、デッドロックが原理的に起きません。さらに retry 構文で「条件が整うまで待つ」ブロッキングも宣言的に書けます。

STM の前提:副作用を戻せること

トランザクションは衝突時にやり直されるため、その中で取り消せない副作用(画面出力・ネットワーク送信・ファイル書き込み)を行うと、二重実行や不整合を招きます。Haskell が STM を型で隔離し、トランザクション内で TVar 操作以外の I/O をコンパイル時に禁止するのはこのためです。また衝突が多発する高競合下では、リトライが空転し続けるライブロックで性能が劣化し得ます。

STM が「不変データ+限定された可変参照」と相性が良いのは、読み取りスナップショットの一貫性を保ちやすいからです。状態を極力変えない設計の利点はイミュータビリティ(不変性)にまとまっています。

同期原理の早見表

モデル状態同期の原理主な失敗モード
共有メモリ+ロック共有排他(ロック)デッドロック・競合
CSP受け渡し通信のランデブーチャネル待ちの停止
アクター私有逐次処理+非同期送信メールボックス膨張
STM共有(TVar)楽観的トランザクションライブロック・副作用

どう選ぶか

唯一の正解はなく、問題の性質で選びます。

  • CSP:処理を段階に分けてデータを流すパイプラインや、明確な受け渡しのある協調に向く。同期点が見えるので順序や流量を制御しやすい。
  • アクター:多数の独立した実体(接続・セッション・デバイス)を疎結合で扱い、障害を隔離したい分散システムに向く。
  • STM:複数の共有データをまとめて不可分に更新したい、かつ副作用が少ない局面に向く。ロックの合成で破綻していたコードを安全にできる。
  • 共有メモリ+ロック:最高性能が要る狭い範囲や、上記モデルの内部実装としては今も現役。
試験・面接で問われる要点

「CSPとアクターの違い」は頻出です。CSPはチャネルが第一級・通信は同期的アクターは宛先が第一級・通信は非同期的——この対比を即答できるように。STM は「楽観的でロック不要・デッドロックは起きないが副作用とライブロックに注意」が核心。いずれも「共有可変状態を減らすことでデータ競合を構造的に避ける」という共通の動機を押さえておくと、横断的な問いに強くなります。

並行処理の入口である待ち合わせの基礎は同期処理と非同期処理で、実行時の状態管理という観点はガベージコレクションで補強できます。モデルを使い分ける前提は一つ——**「同じ可変状態に二者を同時に触れさせない」**という原理を、ロック以外の手段でどう実現するかという発想です。

プログラミング Article

並行性モデル(CSP・アクター・STM)を実務で読む

TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。

解決すること

並行処理

比較で見る軸

難易度: advanced / カテゴリ: プログラミング / タグ数: 5

導入後に効く点

CSPはチャネル(通信路)が主役で送受信が同期点、アクターは各アクターが私有状態を持ちメールボックスで非同期受信。両者とも共有を避けてデータ競合を原理的に消す。

先に潰すリスク

用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。

数字・仕様の読み方
難易度
advanced
カテゴリ
プログラミング
タグ数
5

判断チェックリスト

  • 自社の用途が「並行処理 / CSP」に近いか確認する。
  • 強みである「並行性モデルは「状態をどう共有し、どう同期するか」で分類できる。共有メモリ+ロック/メッセージ受け渡し(CSP・アクター)/トランザクション(STM)が四大潮流。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

並行処理CSPアクターモデルSTM同期並行処理CSPアクターモデル
参考: 公式情報