1. ホーム
  2. c#

[解決済み] スレッドセーフなC#シングルトンパターン

2023-02-23 11:02:07

質問

ここに書かれているシングルトンパターンについて、いくつか質問があります。 http://msdn.microsoft.com/en-us/library/ff650316.aspx

以下のコードは、記事からの抜粋です。

using System;

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

   private Singleton() {}

   public static Singleton Instance
   {
      get 
      {
         if (instance == null) 
         {
            lock (syncRoot) 
            {
               if (instance == null) 
                  instance = new Singleton();
            }
         }

         return instance;
      }
   }
}

具体的には、上記の例では、ロック前とロック後の2回、instanceとnullを比較する必要があるのでしょうか?これは必要なのでしょうか?なぜ最初にロックを実行し、比較を行わないのでしょうか?

以下のように単純化しても問題はないでしょうか?

   public static Singleton Instance
   {
      get 
      {
        lock (syncRoot) 
        {
           if (instance == null) 
              instance = new Singleton();
        }

         return instance;
      }
   }

ロックの実行は高価か?

どのように解決するのですか?

ロックの実行は ひどく を実行することは、単純なポインタチェックと比較した場合、非常に高価です。 instance != null .

ここで表示されているパターンは ダブルチェックロック . その目的は、一度だけ必要になる高価なロック操作を避けることです(シングルトンが最初にアクセスされたとき)。このような実装になっているのは、シングルトンが初期化されるときに、スレッドの競合状態から生じるバグがないことも保証しなければならないからです。

このように考えてください:素の null チェック( lock は、その答えが "yes, the object is already constructed" であるときだけ、正しい使用可能な答えを与えることが保証されています。しかし、答えが "not constructed yet" ならば、本当に知りたかったのはそれが "not constructed yet であるため、十分な情報を持っていないことになります。 であり、他のスレッドではまもなく構築される予定がないことだからです。 ということです。そのため、外側チェックを非常に迅速な初期テストとして使用し、答えが「no"」である場合にのみ、適切でバグのない、しかし高価な手順 (ロックしてからチェック) を開始するのです。

上記の実装はほとんどの場合において十分なものですが、この時点で Jon SkeetのC#におけるシングルトンに関する記事 を読んでみてください。