ここに記載されているシングルトンパターンに関していくつか質問があります。 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;
}
}
ロックの実行は高価ですか?
ロックの実行は、単純なポインターチェックinstance != null
と比較すると、ひどく高価です。
ここに表示されるパターンは double-checked locking と呼ばれます。その目的は、(シングルトンが最初にアクセスされるとき)一度だけ必要になる高価なロック操作を回避することです。実装は、シングルトンが初期化されたときにスレッドの競合状態に起因するバグがないことを保証する必要があるためです。
このように考えてください:裸のnull
チェック(lock
なし)は、その答えが「はい、オブジェクトが既に構築されている」場合にのみ、正しい使用可能な答えを与えることが保証されます。しかし、答えが「まだ構築されていない」場合、あなたが本当に知りたいのは「まだ構築されていないであり、他のスレッドがそれを構築するつもりはない」ということなので、十分な情報がありませんまもなく)。したがって、外側のチェックを非常に迅速な初期テストとして使用し、答えが「いいえ」の場合のみ、バグのない適切な「高価な」手順(ロックしてチェック)を開始します。
上記の実装はほとんどの場合に十分ですが、この時点で C#のシングルトンに関するJon Skeetの記事 を読み、他の選択肢も評価することをお勧めします。
遅延バージョン:
public sealed class Singleton
{
static readonly Lazy<Singleton> lazy = new Lazy<Singleton>(() => new Singleton());
private Singleton() { }
public static Singleton Instance => lazy.Value;
}
.NET 4およびC#6.0(VS2015)以降が必要です。
ロックの実行:かなり安い(nullテストよりもまだ高価です)。
別のスレッドにロックがある場合のロックの実行:ロック中に実行する必要があるすべてのコストを取得し、自分の時間に追加します。
別のスレッドがロックを保持し、他の多数のスレッドもロックを待機している場合にロックを実行します。
パフォーマンス上の理由から、可能な限り最短時間で、常に別のスレッドが必要とするロックを保持する必要があります。
もちろん、幅の狭いロックよりも幅の広いロックについて推論する方が簡単なので、それらから始めて必要に応じて最適化する価値がありますが、パターンに適合する狭い経験から経験と知識から学ぶ場合があります。
(ちなみに、private static volatile Singleton instance = new Singleton()
を単に使用できる場合、またはシングルトンを使用せずに代わりに静的クラスを使用できる場合は、これらの懸念に関してはどちらも優れています)。
その理由はパフォーマンスです。 instance != null
(初めての場合を除いて常にそうです)の場合、高価なlock
を実行する必要はありません。初期化されたシングルトンに同時にアクセスする2つのスレッドは不必要に同期されます。
ほとんどすべての場合(つまり、最初のケースを除くすべてのケース)、instance
はnullになりません。ロックを取得することは、単純なチェックよりもコストがかかるため、ロックする前にinstance
の値を一度チェックすると、最適な最適化になります。
このパターンはダブルチェックロックと呼ばれます: http://en.wikipedia.org/wiki/Double-checked_locking
ジェフリー・リヒターは以下を推奨しています:
public sealed class Singleton
{
private static readonly Object s_lock = new Object();
private static Singleton instance = null;
private Singleton()
{
}
public static Singleton Instance
{
get
{
if(instance != null) return instance;
Monitor.Enter(s_lock);
Singleton temp = new Singleton();
Interlocked.Exchange(ref instance, temp);
Monitor.Exit(s_lock);
return instance;
}
}
}
アプリケーションのニーズに応じて、スレッドセーフなシングルトンインスタンスを熱心に作成できます。これは簡潔なコードですが、@ andasaのレイジーバージョンをお勧めします。
public sealed class Singleton
{
private static readonly Singleton instance = new Singleton();
private Singleton() { }
public static Singleton Instance()
{
return instance;
}
}
次のコード行がアプリケーションの起動時にシングルトンインスタンスを作成するシングルトンの別のバージョン。
private static readonly Singleton singleInstance = new Singleton();
ここでは、CLR(Common Language Runtime)がオブジェクトの初期化とスレッドセーフを処理します。つまり、マルチスレッド環境のスレッドセーフを処理するためにコードを明示的に記述する必要はありません。
「シングルトンデザインパターンでの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");
}