プログラミング
並行性、対話性、可変性:2つを選べ
Concurrency, interactivity, mutability, choose two (n16f.net)
要約
プログラミングにおいて、並行性(複数の処理が同時に進むこと)、対話性(実行中の状態を検査・変更できること)、可変性(データを変更できること)の3つを同時に満たすことは困難であるというトレードオフについて論じています。多くの言語は、これらのうち1つを犠牲にすることで、残りの2つを達成しようとします。例えば、CやRustは対話性を犠牲にし、PythonやRubyは並行性をGILで制限し、Erlangは可変性をメッセージパッシングとコピーで回避しますが、それぞれにパフォーマンスや開発体験上の課題が生じます。
全文翻訳
並行性、対話性、可変性:2つを選べ
2026年7月26日
言語
Common Lispプログラムが実行されており、複数のスレッドがネットワーク接続を待ち受け、他のスレッドがバックグラウンドでデータを処理しているとします。データアクセス競合がないように、あなたは非常に注意を払いました。並行性、チェック。
あなたはEmacsとSLIMEを使ってサーバーに接続し、その状態を検査します。対話性、チェック。
グローバルハッシュテーブルに誤った値が含まれていることに気づきました。問題ありません。簡単なREMHASH式を評価するだけです。可変性、チェック。
残念ながら、別のスレッドがそのハッシュテーブルを読み込もうとしているときに、その式が評価されます。ゲームオーバー。
あなたは並行性を望みます。これは「単位時間あたりに複数の実行スレッドが進捗する」と緩やかに定義されます。なぜなら、プログラムを高速化し、より多くのデータをより速く処理できるからです。しかし、今やあなたのプログラムははるかに複雑になり、デッドロック、スターベーション、メモリ破損などの不快な結果を避けるためにデータアクセスを保護する必要があります。
あなたは対話性を望みます。なぜなら、いつでもプログラムの状態を検査したり更新したりできるのは非常に便利だからです。しかし、今やあなたは、並行スレッドによって読み書きされる可能性のある、任意のデータにいつでもアクセスできます。
あなたは可変性を望みます。なぜなら、それは実用的だからです。しかし、すべてを持つことはできません。
対話性を捨てる
最も一般的な選択肢は対話性を捨てることです。実行中のデータにランダムにアクセスする人がいないため、並行性の問題はありません。C、Rust、Goなど、この道を行く言語は数多くあります。効率的で安全ですが、実行中にシステムを操作する能力を失います。開発中のデバッグや、再起動が常に選択肢ではない複雑なシステムの修正にはフラストレーションが溜まります。
並行性を捨てる
もちろん、複数のスレッドを持つことはできますが、プログラムのどの部分も同時に読み書きすることはできません。PythonとRubyは、GIL(Global Interpreter Lock)を使用して並行性を捨てる言語の良い例です。複数の並行スレッドを実行できますが、PythonやRubyのデータへのアクセスはすべてグローバルロックによって保護されています。PythonシェルやRubyのPryでプログラムを実行し、安全な方法でデータを自由に読み書きできます。残念ながら、それは遅いです。ほとんどの並行プログラムは、すべてのデータアクセスのごく一部に対してのみ同期を必要とします。同期は遅いため、意図的にごく一部に限定します。すべてのアクセスをロックすることには、支払わなければならない非常に現実的なコストがあります。
可変性を捨てる
もしデータを実際には変更できないとしたらどうでしょうか?変数の値を読み取る代わりに、ランタイムが体系的に(そして安全に)値をコピーして返すことができます。スレッドは、再び、値のコピーを送信することによって通信します。もちろん、どこでも任意の値を直接変更することはできません。しかし、メッセージベースのアプローチは、メッセージをスレッドに送信することによって、いつでも安全に状態を更新できることを意味します。Erlangはこの選択をしました。そして、理論上は多くの問題を解決します。並行して安全に動作するスレッド(Erlangでは「プロセス」)を実行できます。なぜなら、共有メモリにアクセスするのではなく、コピーされた値を含むメッセージで通信するからです。そして、システムと自由にやり取りできます。しかし、ここでも無料のランチはありません。データのコピーは遅いです。非常に遅いです。もちろん、データ表現を最適化したり、バイナリブロブ(Erlangでは参照カウントされます)のコピーを避けたりすることはできます。なぜなら、それは耐え難いものになるからです。しかし、それでもひどく遅くなるでしょう。
すべてを持つことはできない
いつものように、言語を選択することはトレードオフのセットを選択することです。パフォーマンスに敏感なプログラムのほとんどは、共有データアクセスを可能な限り制限できるように、対話性を捨てます。対話性の利便性を求める開発者は、グローバルロックの追加コスト、または不変のデータモデルのコストを許容しなければなりません。Common Lispの開発者は、すべてを持つことができると自慢するでしょうが、REPLで行うことは何でもプログラムを壊す可能性があることを常に心に留めておく必要があります。