0

この質問は、次のコード スニペットから来ています。

lambda do
  $SAFE = 2
  puts $SAFE
end .call

puts $SAFE

結果は次のとおりです。

2
0

$SAFEはグローバル変数なので、理解できません。しばらく調べてみたところ$SAFE、本当のグローバル変数ではなく、スレッドローカル変数であることがわかりました。

OK、私はこれを理解できます:

k = Thread.new do
  $SAFE = 2
  puts $SAFE
end

k.run

1000000.times {}

puts $SAFE

しかし待ってください、ブロックはそれを実行するために別のスレッドを開きますか?

4

1 に答える 1

1

いいえ、ブロック (プロシージャ、ラムダ) は独自のスレッドでは実行されません。ここでの問題は、Ruby が$SAFEすべてのメソッド (および proc) 呼び出しの前後でレベルを保存および復元することです。のような別の変数でこれを試すと$FOO、期待どおりの結果が得られます。

> x = ->{ $FOO = 1; puts $FOO }.call; puts $FOO
1
1

これが実装されている場所を次の場所で確認できrb_method_callますproc.c

const int safe_level_to_run = 4 /*SAFE_LEVEL_MAX*/;
safe = rb_safe_level();
if (rb_safe_level() < safe_level_to_run) {
    rb_set_safe_level_force(safe_level_to_run);
}

// ...
// Invoke the block
// ...

if (safe >= 0)
  rb_set_safe_level_force(safe);

セーフ レベルが保存され、4 未満の場合は 4 に設定されます。その後、ブロックが呼び出され、変更前のセーフ レベルが >= 0 の場合は、以前のレベルに戻されます。次のようなものを使用して、これを実際に確認できます。

> puts $SAFE; ->{ puts $SAFE; $SAFE = 1; puts $SAFE }.call; puts $SAFE
0
0
1
0

$SAFEはブロックに向かって 0 であり、ブロックが実行され、ブロック内で 1 になるように変更されているにもかかわらず、ブロックが終了すると 0 に戻ります。

于 2013-10-01T19:31:27.613 に答える