싱글톤 패턴(Singleton Pattern)
- 싱글톤 패턴은 매우 인기있는 디자인 패턴 중 하나입니다.
- 싱글톤은 자원의 공유를 위해 오직 하나만 존재하는 객체를 의미합니다.
- 즉 객체의 중복 생성을 방지함과 동시에 전역으로 공유되어 어디서든 접근할 수 있습니다.
객체의 중복 생성 방지
- 싱글톤 패턴은 프로그램 내에서 오직 하나의 객체만 존재해야 합니다.
- 그렇다면
C++
에서는 언제 객체가 생성될까요?- 생성자를 이용하여 생성된 객체
- 객체를 복제하며 생성되는 객체
- 객체를 대입하며 생성되는 객체
C++
에서는 클래스의 메서드를 정의할 때, 사용자가 정의하지 않으면 자동으로 생성하는 메서드가 존재합니다.
|
|
- 이러한 클래스의 생성자, 복제 생성자, 대입 연산자는
C++
을 더욱 강력하게 만드는 기능입니다. - 하지만 싱글톤 패턴에서는 임시 객체의 생성을 방지해야 할 필요가 있기 때문에, 위와 같은 객체 생성자/연산자를 제거합니다.
|
|
private
접근제어 지시자와delete
키워드를 사용하여 외부에서 객체의 생성과 임시 객체의 생성을 방지합니다.
싱글톤 패턴의 두 가지 구현 방법
- 싱글톤 패턴은
static
키워드를 사용하여 구현합니다.- 일반 클래스에서
static
키워드는 다수의 객체가 존재하더라도, 하나의 공통된 멤버 함수 또는 멤버 변수를 가르킵니다.
- 일반 클래스에서
- 싱글톤 패턴은 객체의 반환 형식에 따라 두 가지 구현 방법이 존재합니다.
- 레퍼런스를 반환하는 방법
- 스마트 포인터를 반환하는 방법
레퍼런스를 반환하는 싱글톤
- 가장 일반적인 싱글톤의 구현 방법입니다.
|
|
- 싱글톤 관련 글을 찾아보면 DCLP(Double-Checked Locking Pattern) 관련 내용을 볼 수 있습니다.
C++11
이후부터static
키워드로 생성된 정적 지역 변수 초기화 코드가 멀티스레드 환경에서도 딱 한번 실행됩니다. 따라서 위와 같은 코드는 멀티스레드 환경에서 안전합니다.
멀티스레드 환경에서 불안전한 싱글톤
- 그렇다면 무엇이 멀티스레드 환경에서 불안전할까요?
|
|
- 멀티스레드 환경에서 위와 같은 싱글톤이 있다고 생각해봅시다.
A
스레드에서Config::getInstance()
를 호출하였습니다.if (m_Instance == nullptr)
조건을 만족하기 때문에(처음 호출되었기 때문에) 싱글톤 객체를 생성하려if statement
로 진입합니다.- 싱글톤 객체가 생성되기 전에,
B
스레드에서Config::getInstance()
를 호출하였습니다. - 이 때,
A
스레드와B
스레드가 호출한Config::getInstance()
는 모두if (m_Instance == nullptr)
조건을 만족합니다. - 따라서 두 개의 싱글톤 객체가 생성됩니다.
- 이와 같이 멀티스레드 환경에서 싱글톤 객체가 두 개 이상 생성될 가능성이 존재합니다.
- 싱글톤 객체는 처음 호출될 때 객체를 생성하기 때문에, 언제 어디서 객체가 생성될지 모릅니다.
- 이러한 이유에서 싱글톤 객체를 코드의 초기화 시점에 호출합니다.
- 이렇게 불안전한 코드를 방지하고자 다음과 같은 기법으로 코드를 작성합니다.
|
|
- 위 코드처럼
m_Instance
의nullptr
확인을 두 번 하기 때문에 DCLP(Double-Checked Locking Pattern)라는 이름이 붙었습니다. - 첫 번째 스레드에서
if (m_Instance == nullptr)
를 만족하면,lock()
을 수행하기 때문에, 다른 스레드에서 접근하더라도std::lock_guard
아래로 넘어가지 않습니다. - 첫 번째 스레드가
m_Instance = new smartConfig();
을 수행하고 종료되면, 기다리던 다른 스레드에서 다시if (m_Instance == nullptr)
확인을 하기 때문에 객체가 중복해서 생성되지 않습니다. - 하지만, 불필요한 확인 절차가 늘어났다는 점과 스레드
lock()
을 수행한다는 점에서 효율이 좋은 코드는 아닙니다. - 따라서
C++11
이상의 환경에서static
키워드를 사용합니다.
스마트 포인터를 반환하는 방법
Mordern C++
에서는 원시 포인터(Raw Pointer) 사용을 권하지 않습니다.- 원시 포인터는 어떠한 포인터로 사용이 가능한 필요 이상의 강력한 포인터입니다.
- 스마트 포인터를 사용한 이벤트 리스너, 핸들러 등을 함께 사용할 경우 객체의 타입이 스마트 포인터여야 할 필요가 있습니다.
|
|