페이지

2012년 12월 23일 일요일

Singleton in C#


가장 많이 쓰이는 디자인 패턴중 하나인 Singleton C#에서 어떻게 구현하는지 알아보자.

Singleton 매우 간단하지만 경우에 따라 구현방법이 여러가지가 있다
멀티스레드를 고려하지 않은 방법늦은 로딩 인스턴스스레드에서 안전한 인스턴싱그리고 간단하지만높은 성능의 방법등..

이러한 방법들이 가지고 있는 공통적인 특징들은 아래와 같다.
  • 다른 외부 클래스에서 인스턴싱 되는것을 막아준다또한 파생 클래스에서도 인스턴싱   없다.왜냐면 Constructor private으로 선언되기 때문이다.
  • 그리고 싱글톤 클래스는 sealed class 구현된다엄격히 말하면 이것은 불필요하다왜냐면 위에서말한데로 외부에서 인스턴싱되거나 파생이 불가능하기 때문이다하지만 JIT(just-in-time compiler)-인터프리트 방식과 정적 컴파일 방식을 혼합한 것임.-  최적화 하도록 도와줄수도 있다.
  • static변수는 생성된 인스턴스를 하나의 레퍼런스로만 사용되도록 한다.
  • public static 인스턴스를 생성한 클래스의 싱글 레퍼런스를 얻는다는 의미이다.

참고로 모든 싱글톤 구현방식은 접근하기 위한 public static Instance  사용하고 있다. 


not thread-safe

// Bad code! Do not use!
public sealed class Singleton
{
    
private static Singleton instance=null;

    
private Singleton()
    {
    }

    
public static Singleton Instance
    {
        get
        {
            
if (instance==null)
            {
                instance = 
new Singleton();
            }
            
return instance;
        }
    }
}

위에 방식은 멀티스레드 상에서는 안전하지 않은 방법이다다른  스레드가 동시에  인스턴스에 접근하게 된다면 if (instance==null) 를 true로 판단할 수 있다.  


Static Initialization
디자인 패턴에서 static Initialization을 피하는 이유중에 하나가 C++ 의 설계에서 static 변수들의 초기화가 모호성을 남기기 때문이다. 

static변수의 경우 어플리케이션이 시작할때 BSS segment에 할당되는데 변수들간의 할당 순서가 규칙이 없기때문이다. 하지만 .NET Framework에서는 변수의 초기화를 핸들링하므로써 이러한 모호성이 해결되어졌다.

public sealed class Singleton
{
   private static readonly Singleton instance = new Singleton();
  
   private Singleton(){}

   public static Singleton Instance
   {
      get
      {
         return instance;
      }
   }
}

위에 코드에서는 처음으로 참조하는 클래스의 멤버가 인스턴스를 생성하도록 구현되었다. 
CLR(The Common Language Runtime)에서 변수의 초기화를 관리한다. 

sealed class는 이 클래스가 다른곳에서 인스턴스가 추가되지 않도록 파생되는 것을 막아준다. 
그리고 readonly 변수는 오직 static initialization 동안에만 할당되도록 할 수 있다. 
다시말해 런타임시 이 변수에 다른 인스턴스로 초기화 할 수가 없다는 얘기이다.

이 구현방식은 앞서 예제와 비슷한다. 변수를 초기화가 CLR방식에 의존하고 있는거 빼고는... 
싱글톤이 가지고 있는 두가지 문제를 해결해주고 있다. 
첫째는 전역적 접근이 가능하다는 것과 둘째는 인스턴싱을 핸들링하므로써 늦은 초기화가 가능하다는 것이다. 
위에서 보는 바와 같이 이 Singleton instance는 private static 멤버변수로 참조되어지기 때문에 어떠한 클래스가 이를 호출하기 전까진 인스턴싱되지 않을 것이다. 
이 방식은 디자인패턴에서의 lazy instantiation 의 형태를 보여준다. 

또한 디자인 패턴에서 말하는 초기화 모호성을 피하기 위해 nondefault constructor를 사용하거나 초기화 되기전 다른 task들을 수행해야했다. 
하지만 .NET Framework 에서는 이러한 문제를 해결하기 위한 initialization을 수행하기때문에 위와 같은 옵션을 사용 할 필요가 없다. .NET의 static initalization을 싱글톤을 구현하기 알맞은 접근방법을 가지고 있다.

참고 사이트:
http://msdn.microsoft.com/en-us/library/ff650316.aspx
http://csharpindepth.com/articles/general/singleton.aspx 

Multithreaded Singleton
아래의 코드는 멀리스레드 환경에서도 안전성을 위해 구현된 방식이다. 글 쓰다 지쳐서 걍 코드만 남김..;;;
더 자세한 내용은 아래 참고사이트를 보면 됨.(-_ -ㅋ)
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;
      }
   }
}


Singleton for Unity 3D

Unity에서 Singleton 패턴을 사용 할 경우 Unity 엔진의 특성에 맞게 신경 써야 할 것들이 있다. 하지만 기본적은 lazy instantiation 같은 개념은 동일하다. 또한 Generic 형식으로 사용하게 되면 원하는 클래스에 상속만 해주면 간단히 Singleton을 만들 수 있다.

아래 링크에 그에 대한 내용들이 있다.



댓글 없음:

댓글 쓰기