1

これは、私が知る限り、厳密に学術的/理論的な問題です。

私が理解しているように (Vance Morrison、http://msdn.microsoft.com/en-us/magazine/cc163715.aspx、「揮発性メモリ アクセスを作成できません...」)、volatileC# のキーワード - とりわけ-- 対応するフィールドの追加の読み取りが「コード モーション」によって導入されるのを防ぎます。

を使用しない C# の二重チェック ロックのバリアント ( http://blogs.msdn.com/b/brada/archive/2004/05/12/130935.aspxvolatile ) があります。

public sealed class Singleton
{
   private static Singleton value;
   private static object syncRoot = new object();

   public static Singleton Value
   {
      get
      {
         if (value == null)
         {
            lock (syncRoot)
            {
               if (value == null)
               {
                  Singleton newVal = new Singleton();
                  Thread.MemoryBarrier(); // ensures that all is ready to publish
                  value = newVal;
               }
            }
         }

         return value;
      }
   }     
}

ただし、(より強力な .NET 2.0 メモリ モデルではなく) ECMA メモリ モデルのみを想定すると、この亜種は失敗するのではないでしょうか? 言い換えると (図 8 の下にあるモリソンの構造の 1 つとの類推によって)、コードが次のようになっているとします。

public sealed class Singleton
{
   private static Singleton value;
   private static object syncRoot = new object();

   public static Singleton Value
   {
      get
      {
         Singleton register = value; // read the field into a register
         if (value == null) // re-read the field (for some unknown reason)
         {
            lock (syncRoot)
            {
               if (value == null)
               {
                  Singleton newVal = new Singleton();
                  Thread.MemoryBarrier();
                  value = register = newVal; // update the register and the field
               }
            }
         }

         return register; // return the reference from the register
      }
   }     
}

2 番目のコード サンプルでは、​​未初期化フィールドをレジスタに読み取った直後にスレッドが横取りされ、他のスレッドがフィールドを初期化した後でスレッドが再開された場合、プロパティは null を返します。誰かがコードでこのように記述した場合 ( というローカル変数を使用register)、壊れていると思います。しかし、「コード モーション」は、実際にはそうでなくても、理論的には、(コード内の) 最初のサンプルを 2 番目のサンプル (実行用) に効果的に変えることができるでしょうか? 私は信じているvolatileこれに対して保護します。また、値をレジスタにコピーした後に実際のシステムがフィールドを再読み取りする理由はわかりませんが、それは私が推進しているポイントではありません。私の質問は本当に次のように要約されると思います: ECMA メモリ モデルの下で、(「コード モーション」の問題として) 無効になる上記の変換 (最初のサンプルを 2 番目のサンプルに変換する) について何かありますか?

2 番目のコード サンプルでは、​​シングル スレッドの観点から、レジスタが読み取られるまでに、レジスタは確実にフィールドと同じ参照を保持します。しかし、スレッド共有フィールドを見ている可能性があることはわかっています。コードモーションがそのフィールドの読み取りを導入することが有効である場合、フィールドの読み取りで異なる値が表示される可能性に遭遇しませんか?レジスターによって現在保持されているものから?そして、volatileこれが問題になることを示すもの (のようなもの) がない場合、コードモーションがこのようにフィールドと (仮想の) レジスタの読み取りをインターリーブするのを実際に防止するものは何か(そうする理由がない場合を除く) ? lock(およびその組み込みMemoryBarrier) は、(おそらく) レジスター (レジスターを表すローカル変数ではなく、本物のレジスター) が古くなる可能性があることを通知する必要がありますが、スレッドがlock(先行する繰り返し読み取りのために) に決して入らない場合はどうなるでしょうか?

.NET メモリ モデルはより強力な保証を提供するため、これをテストするのは明らかに困難です。

4

0 に答える 0