C# Singleton 인스턴스 생성
그러고 보니, Singleton에 대한 글을 몇 개 쓰긴 했군요. ^^
C++에서 싱글톤 구현하기
; https://www.sysnet.pe.kr/2/0/846
DataContext가 thread-safe한 것인가?
; https://www.sysnet.pe.kr/2/0/855
위의 2번째 글에 소개한 링크에서 C#에서의 Singleton 개체 생성에 관한 내용을 확인할 수 있는데요.
C#은 닷넷이 채택한
메모리 모델 덕분에 단순한 DCLP(Double Checked Locking Pattern) 코딩만으로 singleton 개체가 보장됩니다.
static object lockInstance = new object();
static MyObject myInstance;
internal static MyObject Instance
{
get
{
if (myInstance == null)
{
lock (lockInstance)
{
if (myInstance == null)
{
myInstance = new MyObject();
}
}
}
return myInstance;
}
}
Jeffrey Richter의 "CLR via C#" 책을 보면 현재의 CLR이 채택한 메모리 모델이 그럴 뿐 별도로 누군가? 또는 향후에 다른 운영체제에 구현될 CLR의 메모리 모델이 다른 경우에는 적절한 Memory Barrier를 사용해야 한다고 씌여져 있습니다. (아마도 Mono에서는 그래야 될지도 모르겠습니다.)
역시 "CLR via C#" 책에도 나오지만 대개의 경우 그냥 static 생성자에서 개체를 생성하도록 하는 것이 가장 권장되는 방식이기도 합니다. 저 역시 그렇게 많이 사용하고. ^^
static MyObject myInstance = new MyObject(); // .NET에서는 너무나 간단한 Singleton 개체 생성
// (유의할 점: .NET 런타임에 따라 달라지는 정적 필드의 초기화 유무)
그래도 가끔은 DCLP의 "Lazy Initialization"이 그리울 수도 있을 텐데요. 안전한 static 생성자의 구현 방식에 "Lazy Initialization"을 적용시킨 훌륭한 코드가 "
Implementing the Singleton Pattern in C#" 글에서 "Fifth version - fully lazy instantiation" 절에 소개되어 있으니 참고하십시오.
그냥 끝내기 아쉬우니, 잠깐 다소 쓸데없을 것 같은 이야기를 붙여본다면!
결국 Singleton 인스턴스를 생성하는 데에는, 반드시 해당 타입을 한번이라도 접근을 해줘야 하는 것이 중요합니다. 그래서, 때로는 다음과 같이 일부러 빈 static 함수를 만들어 호출해 줄 때도 있습니다.
class MyType
{
static MyType instance = new MyType();
public static void Initialize()
{
}
}
class Program
{
static void Main(string[] args)
{
MyType.Initialize();
}
}
처음에 위와 같이 코드를 작성해 보고 내심 걱정이 되었습니다. 왜냐하면, MyType.Initialize 메서드는 아무런 일도 하지 않기 때문에 DEBUG 빌드의 어셈블리를 실행할 때는 잘 될지 모르지만, RELEASE 빌드의 어셈블리를 실행할 때는 최적화로 인해 메서드 호출이 생략될 수 있기 때문입니다.
물론, 테스트를 해보면 결과가 금방 나오는데 MyType 인스턴스는 정상적으로 릴리스 빌드에서도 생성이 됩니다.
오호~~~ 그렇다면 static 메서드에 대해서는 (IL 코드가 없어도) JIT 컴파일러가 반드시 호출을 해주는 걸까요?
그건 또 아닙니다. 위의 MyType.Initialize는 분명히 JIT 컴파일링 되지 않습니다. (CLR Profiler로 확인해 보면 알 수 있습니다.) 하지만 똑똑한 JIT 컴파일러는 Initialize 호출만 생략할 뿐 해당 타입의 cctor까지는 호출해 줍니다. 즉, (RELEASE 빌드) 실행 시에는 다음과 같은 식으로 동작을 하는 것입니다.
class Program
{
static void Main(string[] args)
{
MyType..cctor(); // 사실 이런 식의 호출이 명시적으로 가능했으면 좋겠습니다.
// 쓸데없이 빈 메서드 만들어 줄 필요가 없으니.
// 혹은, RuntimeHelpers.RunClassConstructor의 힘을 빌려도 되지만 코드가 쓸데없이 어려운 듯한 분위기를 풍깁니다. ^^
}
}
[이 토픽에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]