volatile
필드 없이 구현하는 방법이 있습니다 . 설명하겠습니다 ...
잠금 외부에서 완전히 초기화되지 않은 인스턴스를 얻을 수 있도록 위험한 것은 잠금 내부의 메모리 액세스 재정렬이라고 생각합니다. 이것을 피하기 위해 나는 이것을한다 :
public sealed class Singleton
{
private static Singleton instance;
private static object syncRoot = new Object();
private Singleton() {}
public static Singleton Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
{
var temp = new Singleton();
System.Threading.Thread.MemoryBarrier();
instance = temp;
}
}
}
return instance;
}
}
}
코드 이해
Singleton 클래스의 생성자 내부에 초기화 코드가 있다고 상상해보십시오. 새 객체의 주소로 필드를 설정 한 후 이러한 명령어의 순서가 변경되면 불완전한 인스턴스가있는 것입니다. 클래스에 다음 코드가 있다고 상상해보십시오.
private int _value;
public int Value { get { return this._value; } }
private Singleton()
{
this._value = 1;
}
이제 new 연산자를 사용하여 생성자를 호출한다고 상상해보십시오.
instance = new Singleton();
다음 작업으로 확장 할 수 있습니다.
ptr = allocate memory for Singleton;
set ptr._value to 1;
set Singleton.instance to ptr;
이 지침을 다음과 같이 재정렬하면 어떻게됩니까?
ptr = allocate memory for Singleton;
set Singleton.instance to ptr;
set ptr._value to 1;
차이가 있습니까? 단일 스레드를 생각하면 아니오 . 여러 스레드를 생각하면 예 ... 스레드가 중단 된 직후 set instance to ptr
:
ptr = allocate memory for Singleton;
set Singleton.instance to ptr;
-- thread interruped here, this can happen inside a lock --
set ptr._value to 1; -- Singleton.instance is not completelly initialized
이것이 메모리 액세스 재정렬을 허용하지 않음으로써 메모리 장벽이 피하는 것입니다.
ptr = allocate memory for Singleton;
set temp to ptr;
set ptr._value to 1;
-- memory barrier... cannot reorder writes after this point, or reads before it --
-- Singleton.instance is still null --
set Singleton.instance to temp;
즐거운 코딩 되세요!