.NET에서 이중 점검 잠금
-
23-08-2019 - |
문제
나는 이것을 발견했다 기사 Java에서 Double-Check Locking 패러다임이 깨진 이유에 대해 논의합니다. 패러다임은 .NET (특히 C#)에 유효합니까, 변수가 선언 된 경우 volatile
?
해결책
C#에서 싱글 톤 패턴 구현 세 번째 버전 에서이 문제에 대해 이야기합니다.
그것은 말한다 :
인스턴스 변동성 휘발성을 만드는 것은 명시 적 메모리 장벽 호출과 마찬가지로 작동 할 수 있지만, 후자의 경우 전문가조차도 어떤 장벽이 필요한지 정확히 동의 할 수는 없습니다. 전문가가 옳고 그른 것이 무엇인지 동의하지 않는 상황을 피하려고 노력하는 경향이 있습니다!
저자는 이중 잠금이 다른 전략보다 효과가 적으므로 사용해서는 안된다는 것을 암시하는 것 같습니다.
다른 팁
Double-Checking Locking은 이제 Java와 C#에서 작동합니다 (Java 메모리 모델이 변경되었으며 이것이 효과 중 하나입니다). 그러나, 당신은 그것을 받아야합니다 바로 그거죠 오른쪽. 일을 약간 엉망으로 만들면 스레드 안전을 잃게 될 수 있습니다.
다른 답변이 언급 한 바와 같이, 당신이 구현하는 경우 싱글 톤 패턴 더 좋은 방법이 있습니다. 개인적으로, 두 번 확인 된 잠금 장치와 "잠금"코드 중에서 선택 해야하는 상황에 처한 경우 병목 현상이 발생한다는 실제 증거를 얻을 때까지 매번 잠금을 시작합니다. 스레딩에 관해서는 간단하고 명백히 수정 된 패턴이 많이 가치가 있습니다.
.NET 4.0에는 새로운 유형이 있습니다. Lazy<T>
그것은 패턴을 잘못 이해하는 것에 대한 우려를 빼앗아갑니다. 새로운 작업 병렬 라이브러리의 일부입니다.
MSDN 병렬 컴퓨팅 개발 센터를 참조하십시오. http://msdn.microsoft.com/en-us/concurrency/default.aspx
BTW, .NET 3.5 SP1 용 백 포트가 있습니다 (지원되지 않음). 여기.
Java (및 .NET에서도 가장 가능성이 높음)보다 싱글 톤 초기화를위한 이중 체크 잠금은 완전히 불필요하고 깨진 것입니다. 클래스는 처음으로 사용될 때까지 초기화되지 않기 때문에 원하는 게으른 초기화는 이미 이에 의해 달성되었습니다.
private static Singleton instance = new Singleton();
싱글 톤 클래스에 싱글 톤 인스턴스가 처음 사용되기 전에 액세스 할 수있는 상수와 같은 것들이 포함되어 있지 않으면 이것이 필요한 모든 것입니다.
모든 사람들이 이중 확인 잠금이 나쁜 패턴이라고 말하는 이유는 없지만 코드를 올바르게 작동 시키도록 코드를 조정하지는 않습니다. 제 생각에는 아래 코드는 잘 작동합니다.
이 코드가 Cameron의 기사에 언급 된 문제로 고통받는 지 말해 줄 수 있다면하십시오.
public sealed class Singleton {
static Singleton instance = null;
static readonly object padlock = new object();
Singleton() {
}
public static Singleton Instance {
get {
if (instance != null) {
return instance;
}
lock (padlock) {
if (instance != null) {
return instance;
}
tempInstance = new Singleton();
// initialize the object with data
instance = tempInstance;
}
return instance;
}
}
}
부울을 사용하여 작업하기 위해 두 번 확인 된 잠금을 얻었습니다 (즉, 게으른 초기화를 피하기 위해 원시를 사용하여) :
부울을 사용하는 싱글 톤은 작동하지 않습니다. 메모리 장벽을 거치지 않는 한 다른 스레드간에 보이는 작동 순서는 보장되지 않습니다. 다시 말해, 두 번째 스레드에서 볼 수 있듯이created = true
전에 실행 될 수 있습니다 instance= new Singleton();
나는 왜 이중 체크 잠금에 구현 패턴이 왜 있는지 잘 모르겠습니다 (다양한 언어로 컴파일러 특유의 일을 제외하고). 이 주제에 대한 Wikipedia 기사는 순진한 방법과 문제를 해결할 수있는 가능한 방법을 보여줍니다. 그러나 C#에서 이것만큼 간단한 것은 없습니다.
public class Foo
{
static Foo _singleton = null;
static object _singletonLock = new object();
public static Foo Singleton
{
get
{
if ( _singleton == null )
lock ( _singletonLock )
if ( _singleton == null )
{
Foo foo = new Foo();
// Do possibly lengthy initialization,
// but make sure the initialization
// chain doesn't invoke Foo.Singleton.
foo.Initialize();
// _singleton remains null until
// object construction is done.
_singleton = foo;
}
return _singleton;
}
}
Java에서는 lock () 대신 Synchronized ()를 사용하지만 기본적으로 동일한 아이디어입니다. 싱글 톤 필드가 할당 된 시점의 불일치가있는 경우, 먼저 로컬 스코핑 변수를 먼저 사용한 다음 중요한 섹션을 종료하기 전에 가능한 마지막 순간에 싱글 톤 필드를 할당하지 않겠습니까? 내가 뭔가를 놓치고 있습니까?
@Michael-Borgwardt의 C#과 Java에서 정적 필드는 처음 사용하면 한 번만 초기화되지만 논쟁이 있습니다. 저것 행동은 언어에 따라 다릅니다. 그리고 컬렉션 속성의 게으른 초기화 (예 : user.sessions) 에이 패턴을 자주 사용했습니다.
부울을 사용하여 작업하기 위해 두 번 확인 된 잠금을 얻었습니다 (즉, 게으른 초기화를 피하기 위해 원시를 사용하여) :
private static Singleton instance;
private static boolean created;
public static Singleton getInstance() {
if (!created) {
synchronized (Singleton.class) {
if (!created) {
instance = new Singleton();
created = true;
}
}
}
return instance;
}