여기에 설명 된 싱글 톤 패턴에 대한 몇 가지 질문이 있습니다.
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과 두 번 비교할 필요가 있습니까? 이것이 필요합니까? 먼저 잠금을 수행하고 비교해 보는 것은 어떻습니까?
다음을 단순화하는 데 문제가 있습니까?
public static Singleton Instance
{
get
{
lock (syncRoot)
{
if (instance == null)
instance = new Singleton();
}
return instance;
}
}
잠금을 수행하는 데 비용이 많이 듭니까?
답변
잠금을 수행하는 것은 단순한 포인터 검사와 비교할 때 엄청나게 비쌉니다 instance != null.
여기에 표시되는 패턴을 이중 확인 잠금 이라고 합니다. 그 목적은 한 번만 필요로하는 값 비싼 잠금 작업을 피하는 것입니다 (싱글 톤이 처음 액세스 될 때). 구현은 싱글 톤이 초기화 될 때 스레드 경쟁 조건으로 인한 버그가 없는지 확인해야하기 때문에 그러한 것입니다.
다음과 같이 생각해보십시오. 맨 null체크 (가없는 lock)는 해당 대답이 “예, 객체가 이미 생성되었습니다”일 때만 올바른 사용 가능한 대답을 제공하도록 보장됩니다. 그러나 대답이 “아직 구성되지 않음”이면 충분한 정보가없는 것입니다. “아직 구성되지 않았고 다른 스레드가 곧 구성 할 의도 가 없다는 것”을 알고 싶었 기 때문 입니다. 따라서 외부 검사를 매우 빠른 초기 테스트로 사용하고 답이 “아니오”인 경우에만 적절하고 버그가 없지만 “비싼”절차 (잠금 후 검사)를 시작합니다.
위의 구현은 대부분의 경우에 충분하지만이 시점 에서 다른 대안도 평가하는 C #의 싱글 톤에 대한 Jon Skeet의 기사를 읽어 보는 것이 좋습니다 .
답변
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) 이상이 필요합니다.
답변
잠금 수행 : 상당히 저렴합니다 (널 테스트보다 여전히 비쌉니다).
다른 스레드가 잠금을 수행 할 때 잠금 수행 : 잠금을 수행하는 동안 수행해야하는 작업에 대한 비용이 사용자의 시간에 추가됩니다.
다른 스레드가 잠금을 가지고 있고 수십 개의 다른 스레드도 대기 중일 때 잠금 수행 : Crippling.
성능상의 이유로 가능한 한 가장 짧은 기간 동안 다른 스레드가 원하는 잠금을 항상 갖고 싶어합니다.
물론 좁은 것보다 “넓은”자물쇠에 대해 추론하는 것이 더 쉽기 때문에 넓게 시작하고 필요에 따라 최적화하는 것이 가치가 있지만, 좁은 것이 패턴에 맞는 경험과 친숙 함으로부터 배운 경우가 있습니다.
(부수적으로, 그냥 사용할 수 private static volatile Singleton instance = new Singleton()있거나 싱글 톤을 사용할 수 없지만 대신 정적 클래스를 사용할 수 있다면 둘 다 이러한 문제와 관련하여 더 좋습니다).
답변
그 이유는 성능 때문입니다. instance != null(처음을 제외하고는 항상 그럴 것임) 비용이 많이 드는 경우 lock: 초기화 된 싱글 톤에 동시에 액세스하는 두 개의 스레드가 불필요하게 동기화됩니다.
답변
거의 모든 경우 (즉, 첫 번째 경우를 제외한 모든 경우) instance는 null이 아닙니다. 잠금을 획득하는 것은 간단한 확인보다 비용이 많이 들기 때문에 instance잠금 전 값을 한 번 확인 하는 것은 훌륭하고 무료 최적화입니다.
이 패턴을 이중 검사 잠금이라고합니다. http://en.wikipedia.org/wiki/Double-checked_locking
답변
Jeffrey Richter는 다음을 권장합니다.
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의 게으른 버전을 선호하지만 응용 프로그램 요구 사항에 따라 스레드로부터 안전한 Singleton 인스턴스를 열심히 만들 수 있습니다. 이것은 간결한 코드입니다.
public sealed class Singleton
{
private static readonly Singleton instance = new Singleton();
private Singleton() { }
public static Singleton Instance()
{
return instance;
}
}
