本質的にスレッドセーフではありませんが、正確に何が行われ、何に依存するかによって異なります。また、実装 (「グリーン スレッド」を使用した Ruby 1.8 MRI と Ruby 2 MRI と JRuby など) は、競合状態が発生した場合にどのように発生するかについて重要な役割を果たします。
多くの場合、競合状態は共有データから生じることに注意してください。変数は重要ではありません (スレッドは、再帰メソッドが変数を再利用するのと同様に、別のスレッドのローカル変数を使用しません) が、変数によって名前が付けられたオブジェクトは重要です。(注:proxy
はローカル変数ですが、ローカル変数でproxy.instance
はありません!)
共有データ/オブジェクトを想定した競合状態:
proxy_A = popFromGlobalArray()
proxy_B = popFromGlobalArray()
# assume same object was returned so that proxy_A.equal? proxy_B is true
proxy_A.identifier = Time.now.to_i
proxy_A.active = 1
proxy_B.identifier = Time.now.to_i # such that it is different
proxy_B.active = 1
この時点での結果は同じであるため、これはあまりエキサイティングではありませんが、返されたオブジェクト (proxy_A と proxy_Bの両方が参照するオブジェクト) が識別子変数の割り当て (およびスレッド可視性の伝播) の間で使用されていると想像してください。 - 壊れたコード。
つまり、上記が展開されていると仮定します。
h = {}
# assume same object was returned so that proxy_A.equal? proxy_B is true
proxy_A.identifier = Time.now.to_i
h[proxy_A.identifier] = proxy_A # i.e. used after return
proxy_B.identifier = Time.now.to_i # such that it is different
h[proxy_B.identifier] = proxy_B # i.e. used after return
# now there may be an orphaned key/value.
もちろん、が異なるpopFromGlobalArray
オブジェクトを返すことが保証されている場合、上記は当てはまりませんが、別の問題があります。時間の精度に依存する競合状態です。
proxy_A = popFromGlobalArray()
proxy_B = popFromGlobalArray()
# assume Time.now.to_i returns x for both threads
proxy_A.identifier = x
proxy_B.identifier = x
# and a lost proxy ..
h = {}
h[proxy_A.identifier] = proxy_A
h[proxy_B.identifier] = proxy_B
この話の教訓: 運に頼るな。上記を簡略化して、スレッド間でデータを即座に可視化する際に発生する可能性のある競合状態を示しています。ただし、スレッド間のデータの可視性 (つまり、メモリ フェンスの欠如) により、この問題は最初に見えるよりもはるかに悪化します。