Ruby では、このコードがarray
多くのスレッドによって変更された場合、スレッドセーフではありません。
array = []
array << :foo # many threads can run this code
<<
操作がスレッドセーフでないのはなぜですか?
Ruby では、このコードがarray
多くのスレッドによって変更された場合、スレッドセーフではありません。
array = []
array << :foo # many threads can run this code
<<
操作がスレッドセーフでないのはなぜですか?
実際に MRI (Matz の Ruby 実装) を使用すると、GIL (Global Interpreter Lock) によって純粋な C 関数がアトミックになります。
Array#<<
は MRI で純粋な C コードとして実装されるため、この操作はアトミックになります。ただし、これは MRI にのみ適用されることに注意してください。JRuby ではそうではありません。
何が起こっているのかを完全に理解するには、次の 2 つの記事を読むことをお勧めします。
array
のような操作を適用するときのプログラム変数です<<
。それは次の 3 つのステップで行われます。
したがって、この高レベルの単一操作は 3 つのステップで実行されます。これらのステップの間に、スレッドコンテキストの切り替えにより、他のスレッドが変数の同じ (古い) 値を読み取る可能性があります。そのため、アトミック操作ではありません。
Ruby は非常に高水準の言語であるため、OS レベルで本当にアトミックなものはありません。非常に単純なアセンブリ操作のみが OS レベル (OS 依存) でアトミックであり、Ruby のすべての操作は、単純なものであっても、1 + 1
メソッド ルックアップ、ガベージ コレクション、オブジェクトの初期化、スコープ計算など、実行される数百または数千のアセンブリ命令に対応します。
操作をアトミックにする必要がある場合は、ミューテックスを使用します。
@Linuxios と @TheTinMan からの引用: 高水準言語 (HLL) 操作は一般にアトミックではありません。原子性は、(一般的に) シングルスレッド プログラムでは問題になりません。マルチスレッド プログラムでは、ユーザー (プログラマー) は単一の HLL 操作よりもはるかに高い粒度で推論する必要があるため、アトミックな個々の HLL 操作を使用しても、実際にはそれほど役に立ちません。反対に、HLL 操作をアトミックにするには、少なくとも最新のハードウェアでは前後に数個のマシン命令しか必要ありませんが、静的 (バイナリ サイズ) と動的 (実行時間) のオーバーヘッドが加算されます。さらに悪いことに、コンパイラはアトミック操作間で命令を移動できないため、明示的な原子性はすべての最適化をほとんど無効にします。実質的なメリットなし + 多額のコスト = 非スターター。