Microsoft MVP성태의 닷넷 이야기
싱글톤 공부중 질문이 있습니다. [링크 복사], [링크+제목 복사]
조회: 4396
글쓴 사람
농상
홈페이지
첨부 파일
 

public class Singleton
{
    public static Singleton Instance { get; } = new();

    private Singleton()
    {
        Console.WriteLine("생성자 호출");
    }

    static Singleton()
    {
        Console.WriteLine("정적 생성자 호출");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Singleton instance1 = Singleton.Instance;
        Singleton instance2 = Singleton.Instance;

        if (instance1 == instance2)
        {
            Console.WriteLine($"{nameof(instance1)}과 {nameof(instance2)}는 같은 인스턴스입니다.");
        }
    }
}

간단하게 만든 싱글톤 코드입니다.
일반 생성자와 정적 생성자를 정의했고, 프로퍼티로 읽기 접근만 가능하게 했습니다.
여기서 정적 생성자, 일반 생성자 순으로 호출하고 있습니다.
단지 정적 생성자에서 먼저 new로 생성을 한 후 문자열을 출력하기 때문에 일반 생성자 문자열이 먼저 출력되고, 정적 생성자 문자열이 출력됩니다.

여기서 질문
1. 처음으로 프로퍼티에 접근해서 정적 생성자가 호출될 때, new로 인스턴스를 생성하고 반환하는 과정이 Thread-Safe하기 때문에 위 코드가 Thread-Safe한건가요?
2. 처음으로 프로퍼티에 접근할 때 인스턴스를 생성한다면 Lazy Initialization을 구현한 것 아닌가요? Lazy<T>로 구현한 싱글턴과 어떤 차이가 있나요?








[최초 등록일: ]
[최종 수정일: 10/18/2022]


비밀번호

댓글 작성자
 



2022-10-18 10시09분
1. 정적 필드의 초기화 코드는 정적 생성자에 병합됩니다. 그리고 정적 생성자는 thread-safe하므로 제시한 코드가 thread-safe하게 됩니다.

2. 약간 혼동하시는 것 같은데요, singleton은 엄밀히 Lazy<T>와는 무관합니다. 그리고 제시하신 코드가 엄밀히 lazy하다고 볼 수는 없습니다. 가령, C#에서 정의한 모든 타입을 exe가 실행될 때 자동으로 초기에 생성자를 모두 실행하지는 않습니다. 위의 코드는 단순히 정적 생성자에서 new를 하도록 만든 것 뿐이고 인스턴스를 하나만 유지하도록 생성자를 private으로 처리한 것입니다. (그리고 그런 유형을 우리는 singleton이라고 하고.) 만약 저 코드가 lazy하다고 한다면 C#에서 정의한 다른 모든 클래스들도 사용 전까지는 new를 하지 않았을 것이므로 lazy하다고 봐야 합니다.

일례로, 위의 Singleton 클래스에 정적 메서드를 하나 정의한 경우,

    public static void Test()
    {
        Console.WriteLine("TEST");
    }

Singleton.Test() 메서드를 호출하게 되면,

    Singleton.Test(); // 이 단계에서 개체 생성
    Singleton instance1 = Singleton.Instance;

개체 생성이 필요 없음에도 불구하고 정적 생성자에 병합된 생성자 호출이 발생합니다. 여기서 Instance를 Lazy로 바꿔보면 또 어떨까요?

    public static Lazy<Singleton> Instance { get; } = new Lazy<Singleton>(() => new Singleton());

그럼, 이번에는 Singleton.Test를 호출해도 인스턴스 생성자가 호출되지 않습니다.

그렇다면 singleton이 lazy한 것일까요? lazy한 것이 singleton일까요? ^^
정성태
2022-10-18 12시48분
[농상] 아하 Lazy Initialization에 대해 오해하고 있었군요.

정적 필드는 정적 생성자에 병합되고, 정적 생성자는 Thread-Safe하기 때문에 인스턴스를 Thread-Safe하게 생성한다는 말씀이신데,
그렇다면, int[] arr = new int[5] { 1, 2, 3, 4, 5 }나 List<int> list = new(); 같이 일반적으로 new로 생성하는 것은 Thread-Safe한게 아닌가요?
[guest]
2022-10-18 01시36분
왠지 thread-safe에 대해서도 혼동하시는 것 같은데요, 그 코드를 다중 스레드에서 실행했을 때 부작용이 있을까요?
정성태
2022-10-18 03시09분
[농상] 아하 생각해보니 Thread-Safe하지 않네요.
2개 이상의 스레드가 접근하면 어떤 스레드가 먼저 실행하든 new로 인스턴스를 생성할테니 new 자체가 Thread-Safe한지는 별로 중요하진 않군요
[guest]
2022-10-18 03시31분
맞습니다. ^^ new 자체가 thread-safe 하냐는 것은 별 의미가 없습니다. 결국 thread-safe의 기준은 다중 스레드에서 접근했을 때 상태 값이 안전하냐에 따라 나뉘는 것입니다. 따라서 상태를 가지고 있지 않는 경우라면 언제나 thread-safe한 것이고, 상태를 가지고 있다면 다중 스레드에서의 접근에서 안정적인 상태 변경을 만족해야 thread-safe한 것입니다.

억지를 부리자면.... 이럴 수는 있습니다. (마이크로소프트는 그렇게 구현하지 않았지만 만약 어떤 개발자가 임의로 구현한) 사용자 정의 CLR에서 글로벌하게 단일 GC Heap을 구현하고, 그곳에 개체를 생성한 경우 GC Heap의 그다음 위치를 가리키는 포인터 이동을 한다고 가정해 보겠습니다. 만약 다중 스레드에서 new를 해서 해당 포인터 연산의 결과가 비정상적인 상태로 바뀐다면 그때서야 new는 thread-safe하지 않다고 말할 수 있습니다. 아마도 그런 CLR에서라면 C# 개발자는 모든 new 구문에서 locking을 해야만 했을 것입니다.

그런데, 왜 위의 덧글에서 물어본 코드가 thread-safe하지 않다고 말씀하신 거죠? 완전한 코드를 실어주지 않아서 단정지을 수는 없지만 대개의 경우라면 thread-safe일 텐데요.
정성태
2022-10-18 03시57분
[농상] 싱글톤의 사용성을 증가시켜주는 여러 방법을 보던중 Bill Pugh Solution이 Thread-Safe하다고 하기에 new 자체가 Thread-Safe하기 때문에 그런게 아닌가 추측했습니다.
하지만 결과적으로 인스턴스를 생성하는 과정 자체를 넘어서, 등록하는 과정까지를 정적 생성자가 Thread-Safe하게 보장하기 때문에 static만으로도 Thread-Safe를 보장한다는 걸 알았습니다.
[guest]
2022-10-18 04시08분
[농상] 때문에 스레드 1이 인스턴스를 생성하는 동안 스레드 2는 접근하지 못하고 스레드 1이 인스턴스를 등록한 이후에 정적 필드에 접근할 수 있으므로 다음 스레드부터는 인스턴스가 있음을 보장받을 수 있구요
[guest]
2022-10-18 04시26분
아... 위의 덧글에서의 제 질문은, 바로 그 위에 "int[] arr = new int[5] { 1, 2, 3, 4, 5 }나 List<int> list = new(); 같이 일반적으로 new로 생성하는 것은 Thread-Safe한게 아닌가요?"라고 물어보셨고, 그 아래에서 "아하 생각해보니 Thread-Safe하지 않네요."라고 답변을 하셨길래, 왜 그것이 thread-safe하지 않다고 하신 것인지 물어본 것입니다.

참고로, 언급하신 "Bill Pugh Solution"이 아래의 글에 쓰인 내용과 유사할 것 같은데,

Singletons: Bill Pugh Solution or Enum
; https://dzone.com/articles/singleton-bill-pugh-solution-or-enum

사실 그 내용은 Java에 해당하는 것이고, 게다가 C#/C++은 inner static 클래스의 사용법이 자바와는 다르므로 여기에 적용할 수는 없습니다. 닷넷의 경우라면, 그냥 아래의 글 정도로만 알아두시면 될 듯합니다.

C# Singleton 인스턴스 생성
; https://www.sysnet.pe.kr/2/0/896
정성태

... 16  17  18  19  20  21  22  23  [24]  25  26  27  28  29  30  ...
NoWriterDateCnt.TitleFile(s)
5319권대현4/10/20206206C++(UWP)Dll에서 C#(UWP)Dll 호출이 가능한가요? [1]
5318이승준4/9/20207077동영상 관련 업계 근황? 입니다. [1]
5317kskk...4/7/20209092OpenCV 이용 해상도 설정 질문 입니다.. [3]
5316윤현수4/7/20206331pipe 비동기방식 질문 [1]파일 다운로드1
53154/6/20206992dll파일 로드 원리? 부분에서 도움받고싶습니다. [3]
5314강성욱3/31/20208868메서드 내에서 await 2번 등장할 때의 이해 [11]파일 다운로드1
5313강성욱3/27/20206670await 2번 [1]
53121a2a...3/20/20206446Re raw data write 질문 관련 [1]파일 다운로드1
53111a2a...3/20/20207248Raw data write 관련 질문 [1]파일 다운로드1
5310아스파넬3/15/202072701초에 5회 함수호출 제한 관련 질문입니다. [1]
5309하태3/11/20209160안녕하십니까. c# Winform UI 질문드리겠습니다! [3]파일 다운로드1
5308질문3/9/20207958Window XP SP3 32bit 환경(Window Update 모두 완료)에서 .Net Framework 4.0.3 으로 빌드된 .dll 파일 gacutil.exe 등록 관련 문의 [5]
5306개발자3/9/20206828C#을 통해 인터넷 접속 가능한 이더넷을 선택할 수 있는지요? [1]
5305JaeS...3/9/20206504RAW파일 생성 질문드립니다 [8]파일 다운로드1
5303궁금하당2/27/20208805C#에서 C++ DLL읽기 (아래 글쓴이) [1]
5302궁금하당2/27/202010889C#에서 C++ DLL호출 ('PInvokeStackImbalance') [4]파일 다운로드1
5301질문합니...2/27/20206375소켓 대량 데이터 디자인 문의 [1]
5300nals...2/27/20207005vs2013에서 BinaryFormatter Serialize 후 vs2015에서 Deserialize시 예외 발생 [3]파일 다운로드1
5298질문2/26/20208392.lic 파일에 대해 질문 있습니다. [9]
5297임한승2/26/20205965vs2013에서 BinaryFormatter Serialize 후 vs2015에서 Deserialize시 예외 발생 건 [1]파일 다운로드1
5295나그네2/25/20207359클라이언트 PC 정보 조회관련.. [2]
5294닷넷초보ㅠ2/25/20207343윈폼으로 socket통신프로그램 만든후 release로 디버깅한 실행파일을 다른컴퓨터에서 실행할때 반응이없어요 [1]
5293탱코2/21/20207259검색을 하고 싶은데 어떻게 검색을 해야 할 지 모르겠습니다. [1]
5292아부리2/19/20207149XingApiNet 관련 질문 [3]
5291지현명2/15/20207898JsonConvert.Serialize 하고 Deserialize 할때 간헐적으로 한글 깨짐 현상 문의 합니다. [3]
5290베도빈2/15/20207239직접 제작한 사용자 정의 콘트롤 DLL, 실행파일에 포함시켜 배포할 수 있을까요? [1]
... 16  17  18  19  20  21  22  23  [24]  25  26  27  28  29  30  ...