10

複数のスレッドがすべて同じ関数を呼び出しているとします。

def foo 
  # do stuff ...
end

100.times do |i|
  Thread.new do
    foo
  end
end

2 つ以上のスレッドが現在 内にあるfoo場合、それぞれが 内で同じローカル変数を共有していますfooか?

これは私の2番目の質問に関連しています。スレッドは個別のスタック フレームを持っていますか、それとも 1 つのプロセス内でスタック フレームを共有していますか? 具体的には、複数のスレッドがそれぞれ呼び出されて戻るfoo前に、スタック上に の複数のコピーがあり、それぞれに独自のローカル変数がありますか?それとも、スタック上に のコピーが 1 つしかありませんか?foofoofoo

4

2 に答える 2

5

はい、同じ変数を共有しています。これはスレッドの重要な要素であり、読み取り専用のコンテキストでは問題ありませんが、これらの変数のいずれかに書き込む場合は、スレッドを使用する必要がある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 人だけが通り抜けることができるドアのようなものです。

これで問題が解決することを願っています。

于 2012-05-06T02:23:58.770 に答える
0

メソッド内で定義されたローカル変数は共有されません。ただし、スレッド ブロックのスコープ内にある場合、スレッドが同じオブジェクトのインスタンス変数にアクセスすることは可能です。

例えば:

def foobar
    puts "Foo is defined!" if defined?(foo)=='local-variable'
    foo = 5
end

複数のスレッドによって呼び出された場合、文字列を配置することはありません。

ただし、競合状態が適用されるため、次の例ではミューテックスを同期する必要があります。

foo = {bar:5}
def foobar(value)
    value[:bar]+=5
end
15.times{|i| Thread.new{foobar foo}}

この後、foo[:bar] は 35 の値を含む可能性があります。これは、foobar を呼び出すたびに、ハッシュ foo 内の値が変更されるためです。

于 2012-05-06T08:18:38.383 に答える