93

ここに記載されているシングルトン パターンに関していくつか質問があります: 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;
      }
   }
}

具体的には、上記の例では、ロックの前後でインスタンスを null と 2 回比較する必要がありますか? これは必要ですか?最初にロックを実行して比較してみませんか?

次のように簡略化することに問題はありますか?

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

         return instance;
      }
   }

ロックの実行には費用がかかりますか?

4

10 に答える 10

148

ロックの実行は、単純なポインタ チェックに比べて非常にコストinstance != nullかかります。

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

このように考えてください: 裸のnullチェック ( なしlock) は、その答えが「はい、オブジェクトは既に構築されています」である場合にのみ、正しい使用可能な答えを与えることが保証されています。しかし、答えが「まだ構築されていません」の場合、本当に知りたいのは「まだ構築されておらず、他のスレッドもすぐに構築しようとしていない」ということであるため、十分な情報がありません。したがって、外部チェックを非常に簡単な初期テストとして使用し、答えが「いいえ」の場合にのみ、適切でバグのない「高価な」手順 (ロックしてからチェック) を開始します。

ほとんどの場合、上記の実装で十分ですが、この時点で、C# のシングルトンに関する Jon Skeet の記事を読んで、他の代替案も評価することをお勧めします。

于 2012-09-07T10:38:19.327 に答える
42

Lazy<T>バージョン:

public sealed class Singleton
{
    private static readonly Lazy<Singleton> lazy
        = new Lazy<Singleton>(() => new Singleton());

    public static Singleton Instance
        => lazy.Value;

    private Singleton() { }
}

.NET 4 および C# 6.0 (VS2015) 以降が必要です。

于 2017-05-11T05:57:09.593 に答える
16

ロックの実行: 非常に安価です (null テストよりもさらに高価です)。

別のスレッドがそれを持っているときにロックを実行する: ロック中にまだ実行しなければならないことのコストが自分の時間に追加されます。

別のスレッドがそれを持っていて、他の何十ものスレッドもそれを待っているときにロックを実行する: 障害。

パフォーマンス上の理由から、別のスレッドが必要とするロックを可能な限り短時間で保持したいと常に考えています。

もちろん、狭いロックよりも「広い」ロックについて推論する方が簡単なので、広いロックから始めて、必要に応じて最適化する価値がありますが、経験と慣れから、より狭いロックがパターンに適合することを学ぶ場合があります。

(ちなみに、使用できる場合、private static volatile Singleton instance = new Singleton()またはシングルトンを使用せずに代わりに静的クラスを使用できる場合、これらの懸念に関してはどちらも優れています)。

于 2012-09-07T10:47:02.703 に答える
7

その理由はパフォーマンスです。instance != null(初めての場合を除いて常にそうなる)場合、コストのかかる作業を行う必要はありません。lock初期化されたシングルトンに同時にアクセスする2つのスレッドは、不必要に同期されます。

于 2012-09-07T10:37:07.343 に答える
4

ほとんどすべての場合(つまり、最初の場合を除くすべての場合)、instancenullにはなりません。ロックの取得は単純なチェックよりもコストがかかるため、instanceロックする前にの値を一度チェックすることは、優れた無料の最適化です。

このパターンはダブルチェックロックと呼ばれます:http://en.wikipedia.org/wiki/Double-checked_locking

于 2012-09-07T10:37:35.890 に答える
0

次のコード行がアプリケーションの起動時に Singleton インスタンスを作成する、Singleton の別のバージョン。

private static readonly Singleton singleInstance = new Singleton();

ここでは、CLR (共通言語ランタイム) がオブジェクトの初期化とスレッド セーフを処理します。つまり、マルチスレッド環境でスレッド セーフを処理するためにコードを明示的に記述する必要はありません。

「シングルトン デザイン パターンの Eager ローディングは、オンデマンドではなく、アプリケーションの起動時にシングルトン オブジェクトを初期化し、将来使用できるようにメモリ内に保持する必要があるプロセスではありません。」

public sealed class Singleton
    {
        private static int counter = 0;
        private Singleton()
        {
            counter++;
            Console.WriteLine("Counter Value " + counter.ToString());
        }
        private static readonly Singleton singleInstance = new Singleton(); 

        public static Singleton GetInstance
        {
            get
            {
                return singleInstance;
            }
        }
        public void PrintDetails(string message)
        {
            Console.WriteLine(message);
        }
    }

メインから:

static void Main(string[] args)
        {
            Parallel.Invoke(
                () => PrintTeacherDetails(),
                () => PrintStudentdetails()
                );
            Console.ReadLine();
        }
        private static void PrintTeacherDetails()
        {
            Singleton fromTeacher = Singleton.GetInstance;
            fromTeacher.PrintDetails("From Teacher");
        }
        private static void PrintStudentdetails()
        {
            Singleton fromStudent = Singleton.GetInstance;
            fromStudent.PrintDetails("From Student");
        }
于 2019-07-11T03:48:05.490 に答える