はい、同じ変数を共有しています。これはスレッドの重要な要素であり、読み取り専用のコンテキストでは問題ありませんが、これらの変数のいずれかに書き込む場合は、スレッドを使用する必要があるMutex
ためsynchronize
、変数を変更できるのは常に 1 つだけです。間接的にデータを変更するメソッドを呼び出している場合もあるため、同期が必要かどうかを判断する前に、システムを完全に理解する必要があります。
2 番目の質問については、あなたが何を求めているのか理解できれば、それらは個別のスタック フレームを持っていますが、メモリ内の同じデータを共有しています。
次の例では、ローカル変数zip
は現在のスコープで定義されているため、複数のスレッドで共有されていることを明確にします (スレッドはスコープを変更せず、現在のスコープで別の並列実行スレッドを開始するだけです)。
zip = 42
t = Thread.new do
zip += 1
end
t.join
puts zip # => 43
ここでの参加は私を救いますが、明らかにスレッドにまったく意味がありません。次のようなことをすると危険です。
zip = 42
t = Thread.new do
zip += 1
end
zip += 1
puts zip # => either 43 or 44, who knows?
これは、基本的に 2 つのスレッドzip
が同時に変更を試みているためです。これは、上記のように、ネットワーク リソースにアクセスしたり、数値を増やしたりするときに顕著になります。
ただし、次の例では、ローカル変数zip
はまったく新しいスコープ内に作成されるため、実際には 2 つのスレッドが同じ変数に同時に書き込むことはありません。
def foo
zip = 42
zip += 1 # => 43, in both threads
end
Thread.new do
foo
end
foo
foo
管理されている 2 つの並列スタックがあり、それぞれがメソッド内に独自のローカル変数を持っています。
ただし、次のコードは危険です。
@zip = 42 # somewhere else
def foo
@zip += 1
end
Thread.new do
foo
end
foo
puts @zip # => either 43 or 44, who knows?
これは、インスタンス変数@zip
が関数のスコープ外でアクセスできるfoo
ため、両方のスレッドが同時にアクセスしている可能性があるためです。
「2 つのスレッドが同時に同じデータを変更する」というこれらの問題は、変数を変更するコードのセクションの周りに慎重に配置されたミューテックス (ロック) を使用することによって解決されます。スレッドが作成される前にMutexを作成する必要があります。これは、Mutex の場合、ロックされているかどうかを知るために、両方のスレッドが同じ Mutex にアクセスすることが (設計上) 不可欠であるためです。
# somewhere else...
@mutex = Mutex.new
@zip = 42
def foo
@mutex.synchronize do
@foo += 1
end
end
Thread.new do
foo
end
foo
puts @zip # => 44, for sure!
実行フローがそのMutex#synchronize
行に到達すると、mutex をロックしようとします。成功すると、ブロックに入り、実行を続けます。ブロックが終了すると、ミューテックスは再びロック解除されます。ミューテックスがすでにロックされている場合、スレッドは再び解放されるまで待機します...事実上、一度に 1 人だけが通り抜けることができるドアのようなものです。
これで問題が解決することを願っています。