2

volatileキーワードは、特定のコンパイラの最適化からフィールドを保護するために使用されます。

不揮発性フィールドの場合、命令を並べ替える最適化手法は、lock-statement (セクション 8.12) によって提供されるような同期なしでフィールドにアクセスするマルチスレッド プログラムで、予期しない予測不可能な結果につながる可能性があります。

ただし、MSDN は、lock最適化保護を で使用されるオブジェクトのみに適用するかexpression、またはstatement_block.

コードのブロックがある場合:

lock(obj_something){
    boolFoo = true;
    if(boolIsFriday)
        doSomething(anything);
}

boolFooおよび暗黙は揮発性ですかboolIsFriday(宣言されていなくてもvolatile)?

4

5 に答える 5

3

これは難しいトピックであり、Joe Duffy の本を読むことから始めることをお勧めしますConcurrent Programming on Windows。1000 ページの純粋な知識です。

あなたの質問への答え: いいえ、変数は「暗黙的に揮発性」にはなりません

が提供するものlock- 相互排除は別として - 取得と解放の両方でのフェンスです。

これは、コンパイラ、ジッター、CPU、またはその間にあるものによって適用できる並べ替えの最適化にいくつかの制限が課されることを意味します。

特に:

ロックを取得すると、メモリアクセスがフェンスの に移動するのを防ぎます。
ロックを解除すると、メモリ アクセスがフェンスの後に移動できなくなります。


あなたの例ではboolFoo、ロックが取得される前に、どのスレッドも書き込みを監視できません。

同様に、ロックが取得される前に読み取られた値の読み取りboolIsFridayと使用はできません。anything

解放boolFoo時には、メソッドによって実行される書き込みと同様に、ロックが解放されたときに書き込みが可視である必要がありますdoSomething


あなたのコメントへの回答: これは、他のスレッドの並べ替えを妨げるものではありません。

これを行う別のスレッドがある場合B:

// do some work while your lock code is running
for ( int i = 0 ; i < ( int ) 1e7 ; i++ ) ;

var copyOfBoolFoo = boolFoo;

であること可能copyOfBoolFooですfalse

これは、forループとロック コードが実行される前に何かが先を見越して、値を読み取っboolFooてキャッシュすることを決定する可能性があるためです。

Bこれを防ぐスレッドには何もないため、発生する可能性があります。

boolFooin threadの読み取りの前にフェンス (例: ロック!) を配置すると、最新の値を読み取るBことが保証されます。

于 2013-04-05T17:53:00.020 に答える
2

ただし、MSDN は、lock が最適化保護を式で使用されるオブジェクトだけに適用するのか、statement_block 内のすべてのステートメントに適用するのかを明らかにしていないようです。

後者。

boolFoo と boolIsFriday は暗黙的に揮発性ですか (揮発性と宣言されていなくても)?

そうではありません。の考え方はvolatile、オブジェクトがアクセスされるたびにメモリバリアが適用されるということです。は、オブジェクトのその1 回の使用lockにのみメモリ バリアを適用します。そのため、ステートメントの内部と外部からの (特定のタイプの定義された) 操作を混合する最適化はありませんが、他の何かがブロックなしでその変数にアクセスすると、それでも問題が発生する可能性があります。locklock

于 2013-04-05T17:46:16.320 に答える
2

あなたの質問に答えるために、あなたの例を詳しく見てみましょう。読み取りがいつ行われるかをより明確にするために、コードを少し調整しますboolIsFriday。動作は同じです。これが私たちが持っているものです。

lock(obj_something)
{
    boolFoo = true;
    var register1 = boolIsFriday;
    if (register1)
    {
        var register2 = anything;
        doSomething(register2);
    }
}

次に、このコードを矢印表記で装飾して、メモリ バリアがどこにあるかを視覚化します。↑ 矢印と ↓ 矢印を使用して、リリース フェンス取得フェンスをそれぞれ表します。揮発性書き込みにはリリース セマンティクスがあるため、書き込みのに ↑ が配置されます。揮発性読み取りはセマンティクスを取得するため、読み取りの後に↓ が配置されます。矢じりはすべてのものを遠ざけるものと考えてください。他の読み取りまたは書き込みは、矢印の頭を超えて上下に移動することはできません。今説明した用語で正確に考えると、これはメモリバリアが行っていることを正確に反映しているはずです. また、たまたま仕様に一致しているだけです。

始める前に、さらにいくつか言及する必要があります。まず、他のメモリ アクセスは矢印の頭を越えて移動することはできませんが、矢印の尾を越えて移動することはできます。繰り返しますが、これは仕様の文言に準拠しています。次に、メモリバリアが生成されると、それらは所定の位置にロックされます。ただし、揮発性の読み取りや書き込みなど、それらを生成するメカニズムは自由に移動できます。つまり、矢印は移動できませんが、対応する読み取りまたは書き込みは移動できます。3 番目に、alockは完全なメモリ バリアを生成します。


ロック内のメモリアクセスは暗黙的に揮発性ですか?

この質問に答えるために、例に矢印表記を追加します。

↑
lock(obj_something)
↓
{
    boolFoo = true;
    var register1 = boolIsFriday;
    if (register1)
    {
        var register2 = anything;
        doSomething(register2);
    }
↑
}
↓

矢印が配置されている場所に注意してください。他のメモリジェネレーターがない代わりに、lockそれらを注入するだけです。今明らかになるべきいくつかの事実があります。

  1. ロック ブロックの外側にあるメモリ アクセスは、ブロックに移動できません。
  2. ロック ブロックのメモリ アクセスはブロックに移動できません。
  3. ロック ブロック内のすべてのメモリ アクセスは、ポイント #2 が満たされている限り、自由に移動できます。

これにより、質問の 1 つにすぐに答えられるはずです。いいえ。からボラティリティ セマンティクスを暗黙的に継承しませboolFooん。現在、いくつかの制約はありますが、彼らはまだ自由に動き回ることができます。boolIsFridaylock

boolFooでは、とboolIsFridayの両方が としてマークされている場合はどうなるでしょうかvolatile。何が起こるか見てみましょう。

↑
lock(obj_something)
↓
{
    ↑
    boolFoo = true;
    var register1 = boolIsFriday;
    ↓
    if (register1)
    {
        var register2 = anything;
        doSomething(register2);
    }
↑
}
↓

矢印が配置されていることに注意してください。書き込みの前に↑の矢印があり、boolFoo他のメモリアクセスが揮発性書き込みを介してフロートダウンできないことを示しています。同様に、 の読み取りの後に ↓ 矢印がありboolIsFriday、揮発性読み取りによって他のメモリ アクセスがフロートアップできないことを示します。現在明らかになっている2つの特異な事実があります。

  1. への書き込みboolFooと読み取りがboolIsFriday入れ替わる可能性があります。それを防ぐ矢印がないことに注意してください。
  2. への書き込みboolFooは、ブロックの最後までフロートする可能性がありlockます (もちろん、メモリ バリアが生成されないと仮定しdosomethingます。それ以上下がらないようにするロック ブロックを終了するのは ↑ 矢印です。
  3. の読み取りはboolIsFriday、ある時点で の読み取りと場所を交換する必要があるため、それほど遠くまでフロートダウンすることはできませんanything。しかし、それが起こった場合、意味的にはanything、揮発性の読み取りを表す ↓ を過ぎて浮かんでいるのと同じになります。動きが妨げられていることはすでに述べました。

ロックは式またはブロックの内容の最適化を制限しますか?

そして最後に、あなたが持っていた他の質問に答えます。キーワードは、lockロック式とロック ブロック自体の内容の両方に何らかの影響を与えます。これがどのように起こるかを見てみましょう。面白い表現のロック文を考案しました。

object foo;
lock (foo=bar)
{
  // contents of lock here
}

私が何をしたか分かりますか?式に代入を入れました。そして、ここで何が起こっているのかをよりよく理解するために再編成すると、次のようになります。

object foo;
foo = bar;
object expression = foo;
↑
lock (expression)
↓
{
  // contents of lock here
↑
}
↓

ここで、次のことに注意してください。

  1. ステートメントによって ↑ が生成されるため、 write tofooはフロートダウンできません。lockこれは、ロック式が影響した 1 つの方法です。
  2. ロック ブロックのコンテンツには、その動きに制約が課されるようになったことについては既に説明しました。

あなたの質問に答えるために、lockキーワードはロック式とロックブロックの内容の両方に最適化の制約を課します。

于 2013-08-09T14:57:20.500 に答える
2

いいえ、lock ステートメントのみが直接、「ロック」しているオブジェクトへのアクセスを保護します。

要点は、オブジェクトを複数の場所でロック ソースとして使用する場合、ロックを所有しているため、(ロック スコープ内の) それらのコード ブロックの 1 つだけを一度に実行できるということです。

boolFoo同じオブジェクトのロック内で排他的にアクセスされる場合にboolIsFridayのみ保護されます (この場合はobj_something)

于 2013-04-05T17:43:24.337 に答える
0

ブロック内のすべてのステートメントは、ユニットとして、同じロックを保持するブロック内で実行される他のステートメントに関して、ユニットとして適切に順序付けられていることが保証されます。それ以上でもそれ以下でもありません。

あなたが持っている場合:

lock (foo) // in one thread
{
  x();
  y();
}

lock (foo) // in another
{
  a();
  b();
}

x y a b実行順序がまたはのいずれかであることが保証されますa b x y

x a b yx a y b、 またはxaが同時に走って爆破するのを防ぎます。

于 2013-04-05T17:48:06.613 に答える