'Singleton'에 해당되는 글 2건

  1. 2015.11.02 Singleton Pattern
  2. 2015.06.02 Singleton

Singleton Pattern

Programming/Etc 2015. 11. 2. 17:01
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

5. 싱글턴 패턴

  • 싱글턴패턴은 인스턴스가 하나 뿐인 특별한 객체를 만들 수 있게 해주는 패턴이다.
  • 어떤 용도로 쓰는 건가?
    • 스레드 풀이라던가, 캐시, 대화상자, 사용자설정, 디바이스드라이버 등등 객체가 전체프로그램에서 오직 하나만 생성되어야 하는 경우에 사용한다.
  • 그럼 전역변수로 static 으로 선언해서 사용하면 되지 않느냐?
    전역 변수로 객체를 생성하면 어플리케이션이 실행 될 때 객체가 생성될 것이다.(P208 맨밑줄)
    그 객체가 자원을 많이 차지 한다면 사용도 되기전에, 괜히 자원만 차지한다. 사용하지 않는다면 아무 쓸 데 없는 객체가 되겠지.

고전적인 싱글턴 패턴 구현법

  • 조심하세요.. 이 코드에 문제가 있다는 것을 알게 될 것입니다.
public class Singleton {
  
  //private으로 Sinleton클래스의 유일한 인스턴스를 저장하기 위한 정적 변수를 선언
  private static Singleton uniqueInstance;

  //생성자를 private로 선언했기 때문에 Singleton에서만 클래스를 만들 수 있다.
  private Singleton() {}

  //클래스의 인스턴스를 만들어서 리턴해 준다.
  public static Singleton getInstance() {
    if(uniqueInstance == null) {
      uniqueInstance = new Singleton();
    }
    return uniqueInstance;
  }

}

싱글턴 패턴의 정의

  • 싱글턴 패턴은 해당 클래스의 인스턴스가 하나만 만들어진다,
  • 어디서든지 그 인스턴스에 접근할 수 있도록 한다.
  • 클래스에서 자신의 단 하나뿐인 인스턴스를 관리하도록 만들면 된다.

멀티스레딩 문제 해결 방법

  • getInstance()를 동기화시키기만 하면 멀티스레딩과 관련된 문제가 간단하게 해결된다.
public class Singleton {
  private static Singleton uniqueInstance;
  // 기타 인스턴스 변수
  private Singleton() {}
  
  //synchronized 키워드만 추가하면 두 스레드가 이 메소드를 동시에 실행시키는 일은 일어나지 않게 된다.
  public static synchronized Singleton getInstance() {
    if (uniqueInstance == null) {
       uniqueInstance = new Singleton();
    }
    return uniqueInstance;
  }
// 기타 메소드
}
  • 이렇게 하면 문제가 해결되긴 하겠지만, 동기화를하면 속도 문제가 생기지 않나?
    동기화는 불필요한 오버헤드만 증가시킬 수 있다.

더 효율적인 방법은 없을까요?

1. getInstance()의 속도가 그리 중요하지 않다면 그냥 내비 둔다.

  • 메소드를 동기화하면 성능이 100배 정도 저하된다는 것은 기억해 두자
  • 만약 getInstance( )가 애플리케이션에서 병목으로 작용한다면 다른 방법을 생각해봐야 한다.

2. 인스턴스를 필요할 때 생성하지 말고, 처음부터 만들어 버린다.

public class Singleton {
  private static Singleton uniqueInstance = new Singleton();

  private Singleton() {}

  public static Singleton getInstance() {
    return uniqueInstance;
  }
}
  • 이런 접근법을 사용하면 클래스가 로딩될 때 JVM에서 Singleton의 유일한 인스턴스를 생성해 준다.

3. DCL(Double-Checking Locking)을 써서 getInstance()에서 동기화되는 부분을 줄인다.

  • DCL(Double-Checking Locking)을 사용하면, 일단 인스턴스가 생성되어 있는지 확인한 다음, 생성되어 있지 않았을 때만 동기화를 할 수 있다.
  • volatile 키워드를 사용하여 멀티스레딩을 쓰더라도 uniqueInstance 변수가 Singleton 인스턴스로 초기화 되는 과정이 올바르게 할 수 있다.
  • DCL은 자바 1.4 이전 버전에서는 쓸 수 없다
public class Singleton {
  private volatile static Singleton uniqueInstance;

  private Singleton() {}

  public static Singleton getInstance() {
    if (uniqueInstance == null) {
      //이렇게 하면 처음에만 동기화 된다
      synchronized (Singleton.class) {
        if (uniqueInstance == null) {
          uniqueInstance = new Singleton();
        }
      }
    }
    return uniqueInstance;
  }
}

핵심 정리

  • 어떤 클래스에 싱글턴 패턴을 적용하면 애플리케이션에 그 클래스의 인스턴스가 최대 한 개 까지만 있도록 할 수 있다.
  • 싱글턴 패턴을 이용하면 유일한 인스턴스를 어디서든지 접근할 수 있도록 할 수 있다.
  • 자바에서 싱글턴 패턴을 구현 할 때는 private 생성자와 정적 메소드, 정적 변수를 사용 한다.
  • 다중 스레드를 사용하는 애플리케이션에서는 속도와 자원 문제를 파악해보고 적절한 구현법을 사용해야 한다.
  • DCL을 사용하는 방법은 자바2 버전 5(자바 1.5)보다 전에 나온 버전에서는 쓸 수 없다는 점에 주의.
  • 클래스 로더가 여러 개 있으면 싱글턴이 제대로 작동하지 않고, 여러 개의 인스턴스가 생길 수 있다.


출처 : http://wiki.gurubee.net/pages/viewpage.action?pageId=1507403

Posted by 역시인생한방
,

Singleton

Programming/C / C++ 2015. 6. 2. 17:54
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

C++에서 Singleton의 구현은 몇가지 방법이 있다.

이중 내가 주로 사용하는 방법은 아래의 방법이다.

 

class A

{

private:

  static A* m_Instance;

public:

  static A* Instance()

  {

    if( m_instance == NULL )

    {

      m_Instance = new A();
    }

    return m_Instance;

  }

};

 

위와 같이 구현된 형태가 내가 사용하는 Singleton의 기본 형태이다.

 

여기에 몇가지 추가 기능이 들어간다.

 

첫번째는 thread-safe를 위해서 Lock을 사용하는 것이다.

m_Instance가 여러 쓰레드에서 접근하기 때문에 두개 이상 생기는 것을 방지하기 위함이다.

그렇지만 매번 NULL 체크를 할 때마다 Lock을 잡는 것은 성능상 큰 문제가 있다.

그래서 사용하는 것이 double checked lock 패턴이다.

구현은 아래와 같다.

 

inline A* A::Instance()

{

  if( m_Instance == NULL )        // #1

  {

    m_Lock.Aquire();

    if( m_Instance == NULL )      // #2

    {

      m_Instance = new A();
    }

    m_Lock.Release();
  }

  return m_Instance;
}

이렇게 사용하려면 m_Instance는 아래와 같이 volatile로 선언이 바뀌어야 한다.

 

A * volatile A::m_Instance;

 

이것이 안들어가게 되면 Complier optimization에 의해서 #1에서 읽은 값과 #2에서 읽은 값이 항상 같게 되어서 Lock을 잡는 의미가 없어질 가능성이 있다.

당연히도 volatile을 쓰면 안정적인 동작이 되지만 성능은 떨어지게 된다.

Singleton은 Instance() 콜이 매우 잦을 가능성이 많기 때문에 성능이 저하되는 것은 안될 일이다.

이것을 해결하기 위해서 추가적인 함수를 사용해서 volatile을 제거한다.

 

class A

{

...

  static A* CreateInstance();

...
};

 

inline A* A::Instance()

{

  if( m_Instance != NULL )

  {

    return m_Instance;        // #3

  }
  return CreateInstance();
}

 

//-------------

// in .cpp

A* A::CreateInstance()

{

  m_Lock.Aquire();

  if( m_Instance == NULL )

  {

    m_Instance = new A();     // #4

  }

  m_Lock.Release();

  return m_Instance;
}

 

이렇게 inline이 아닌 함수로 생성을 위임하는 것을 통해서 volatile을 제거할 수 있다.

inline이 아니기 때문에 CreatInstance() 안에서 m_Instance를 새로 읽어올 것을 보장할 수 있다.

(물론 특정한 컴파일 옵션에 의해서 강제로 인라인 화 될 경우 어떻게 될지는 모르나 그렇게 까지 쓸경우는 없을 것이다.)

 

위의 예에서 Instance() 함수는 처음 생성될 때를 제외하고는 if 조건 하나를 조사하고 #3이 불리는 형태로 진행이 된다.

이것은 인라인화가 될 것이고 CPU의 branch prediction까지 대부분을 적중 시킬 것이다.

그렇기 때문에 실제 A::Instance()를 콜하면 수행되는 코드는 m_Instance를 직접 참조하는 것과 거의 차이가 나지 않을 것이다.

 

마지막으로 추가되는 기능은 프로그램 종료시 메모리를 정리하는 부분이다.

즉 new A() 로 생성된 m_Instance를 지워야 한다.

이것은 stdlib에 존재하는 atexit() 함수를 이용해서 구현하면 된다.

#4의 부분에 atexit()로 자신의 instance를 삭제하는 함수를 등록한다.

 

class A

{

...

private:

  static void  __cdecl DestoryInstance();

...
};

 

void __cdecl A::DestoryInstance()

{

  delete m_Instance;
}

#4 다음 줄에 아래 추가

atexit(&DestoryInstance);

 

이렇게 되면 프로그램 종료직전에 DestoryInstance() 가 불려서 할당된 객체를 삭제하게 된다.

[출처] Singleton|작성자 솔라리스



출처 : http://kimsk99.blog.me/50007325992

참고 : http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf

'Programming > C / C++' 카테고리의 다른 글

Windows Named Pipe 구현 간단 정리  (0) 2015.06.11
GetSystemMetrics  (0) 2015.06.04
URLDownloadToFile example  (0) 2015.05.26
GetCurrentDirectory() vs GetModuleFileName()  (0) 2015.05.22
빠른 음수화  (0) 2015.02.07
Posted by 역시인생한방
,