본문 바로가기

개발 Note/C++11,14 (modern C++)

Singleton pattern and std::call_once

반응형



Singleton pattern은 객체의 인스턴스가 process내에서 1개만 생성되도록 하는 것입니다.


이렇게 하기 위해서는 global 객체를 하나만 유지하고, 일반적으로 getInstance() 와 같은 class의 스테틱 메소드(static method)를 통해서 생성된 instance를 얻어가는 구조입니다.


주로 XXXX Manager 와 같은 management instance 류 들이 singleton으로 작성됩니다.



가장 simple한 구현체는 아래와 같은 형식입니다.


class Singleton

{

  public:

    static Singleton* getInstance();

  private:

    Singleton()=default;

};


Singleton* 

Singleton::getInstance();

{

    static Singleton inst;

    return &inst;

}

또는 

Singleton* 

Singleton::getInstance();

{

    static Singleton* inst = new Singleton();

    return inst;

}

또는 

static Singleton* __inst = nullptr;

Singleton* 

Singleton::getInstance();

{

    if(__inst == nullptr)

    {

       __inst = new Singleton();

    }

    return __inst;

}


하지만, 이렇게 작성된 코드는 객체 생성 시점에 쓰레드 세이프(thread safe)하지 않기 때문에 , single thread환경에서는 이슈가 없겠지만,

multi-thread환경에서는 이슈가 생길 가능성이 다분합니다.


이를 위해서 각 platform 환경에 맞춰서 thread safe 형식을 추가하여 작업을 해야 하는 상황이 발생하죠.

pthread 를 지원하는 환경이면, pthread_once 와 같은 함수들 말이죠.



C++ 11에서 부터는 언어 차원에서 pthread_once 와 같은 기능을 할 수 있는 기능을 지원하게 되었습니다.

std::call_once() 입니다. (http://www.cplusplus.com/reference/mutex/call_once/?kw=call_once)


이 call_once를 이용하면 platform과 연관성을 줄이면서 Singleton 디자인 패턴(design pattern)을 완성 할 수 있습니다.


[std::call_once를 이용한 Singleton pattern 구현]

#include <new>

#include <memory>

#include <mutex>

#include <stdio.h>


class Singleton

{

public:

    static Singleton& getInstance();


    void log(){

        printf("instance: %p", __instance.get());

    }

    ~Singleton(){

        printf("delete instance : %p", this);

    }

private:

    Singleton() = default;

    

    Singleton(const Singleton &) = delete;

    Singleton &operator=(const Singleton &) = delete;


    static std::unique_ptr<Singleton> __instance;

    static std::once_flag __once;

};



std::unique_ptr<Singleton> Singleton::__instance;

std::once_flag Singleton::__once;


Singleton& 

Singleton::getInstance()

{

    std::call_once(__once, []() {

      __instance.reset(new Singleton);


      printf("create instance : %p\n", __instance.get());


    });


    return *__instance.get();

}


void 

test_std_once()

{

    Singleton& ins= Singleton::getInstance();    

    ins.log();

}