18

最近、メモリバリアと並べ替えの問題について読みましたが、今では混乱しています。

次のシナリオを検討してください。

private object _object1 = null;    
private object _object2 = null;
private bool _usingObject1 = false;

private object MyObject
{
    get 
    {
        if (_usingObject1)
        {
            return _object1;
        }
        else
        {
            return _object2;
        }
    }
    set 
    {
        if (_usingObject1)
        {
           _object1 = value;
        }
        else
        {
           _object2 = value;
        }
    }
}

private void Update()
{
    _usingMethod1 = true;
    SomeProperty = FooMethod();
    //..
    _usingMethod1 = false;
}
  1. Updateメソッドで; プロパティを取得または設定する前に、_usingMethod1 = trueステートメントは常に実行されますか?または再注文の問題により、それを保証することはできませんか?

  2. 私たちはvolatileのように使用する必要があります

    private volatile bool _usingMethod1 = false;
    
  3. canを使用lock;すると、ロック内のすべてのステートメントが次のような順序で実行されることが保証されます。

    private void FooMethod()
    {
        object locker = new object();
        lock (locker)
        {
            x = 1;
            y = a;
            i++;
        }
    }
    
4

2 に答える 2

35

メモリバリアの問題は非常に複雑です。それは時々専門家をつまずかせさえします。私たちがメモリバリアについて話すとき、私たちは実際に2つの異なるアイデアを組み合わせています。

  • フェンスの取得:他の読み取りと書き込みがフェンスのに移動できないメモリバリア。
  • リリースフェンス:他の読み取りと書き込みがフェンスのに移動できないメモリバリア。

2つのうち1つだけを作成するメモリバリアは、ハーフフェンスと呼ばれることもあります。両方を作成するメモリバリアは、フルフェンスと呼ばれることもあります。

volatileキーワードはハーフフェンスを作成します。揮発性フィールドの読み取りには取得セマンティクスがあり、書き込みにはリリースセマンティクスがあります。つまり、読み取り前または書き込み後に命令を移動することはできません。

キーワードは、lock両方の境界(入口と出口)にフルフェンスを作成します。つまり、各境界の前後で命令を移動することはできません。

ただし、1つのスレッドのみに関係する場合は、このすべての問題があります。そのスレッドによって認識される順序付けは、常に保持されます。実際、その基本的な保証がなければ、プログラムは正しく機能しません。本当の問題は、他のスレッドが読み取りと書き込みをどのように認識するかということです。それはあなたが心配する必要があるところです。

だからあなたの質問に答えるために:

  1. シングルスレッドの観点から...はい。別のスレッドの観点から...いいえ。

  2. 場合によります。それはうまくいくかもしれませんが、私はあなたが何を達成しようとしているのかをよりよく理解する必要があります。

  3. 別のスレッドの観点から...いいえ。読み取りと書き込みは、ロックの境界内を自由に移動できます。それらはそれらの境界の外に移動することはできません。そのため、他のスレッドもメモリバリアを作成することが重要です。

于 2010-05-16T20:42:02.297 に答える
4

volatileキーワードは、ここでは何も実行しません。保証は非常に弱く、メモリバリアを意味するものではありません。コードには別のスレッドが作成されていることが表示されないため、ロックが必要かどうかを推測するのは困難です。ただし、2つのスレッドがUpdate()を同時に実行し、同じオブジェクトを使用できる場合は、難しい要件です。

投稿されたロックコードは何もロックしないことに注意してください。各スレッドには、「locker」オブジェクトの独自のインスタンスがあります。コンストラクターまたは初期化子によって作成された、クラスのプライベートフィールドにする必要があります。したがって:

private object locker = new object();

private void Update()
{
    lock (locker)
    {
        _usingMethod1 = true;
        SomeProperty = FooMethod();
        //..
        _usingMethod1 = false;
    }
}

SomePropertyの割り当てにも競合が発生することに注意してください。

于 2010-05-16T17:44:34.070 に答える