여기에 설명 된 싱글 톤 패턴에 대한 몇 가지 질문이 있습니다.
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;
}
}