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

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
정성태

... 61  62  63  64  65  66  [67]  68  69  70  71  72  73  74  75  ...
NoWriterDateCnt.TitleFile(s)
914오병태10/11/201015425감사드립니다. 염치없지만 또 한번 문의드립니다. [2]
912오병태10/11/201015719윈도우즈 인증서 관련해서 문의를 드립니다.
913정성태10/11/201019565    답변글 [답변]: 윈도우즈 인증서 관련해서 문의를 드립니다.
911나그네.10/6/201017964안녕하세요.. openssl 관련하여...혹시.. [2]
910윤용한9/17/201017558Visual Studio 2010으로 만든 Setup 파일을 Win2000 SP4에 설치 되게 할 수 없나요? [1]
908김재영9/7/201015796솔루션에 구성된 프로젝트의 버젼만 통합할려면 어떤 방법이 있습니까?
909정성태9/7/201017197    답변글 [답변]: 솔루션에 구성된 프로젝트의 버전만 통합할려면 어떤 방법이 있습니까? [1]
906임동찬8/19/201017365디버깅 중 이해할 수 없는 상황이 [1]
904김영태8/18/201018842C# Interop 관련 질문입니다. [1]
905김영태8/19/201020386    답변글 [답변]: C# Interop 관련 질문입니다. [4]
903임동찬8/17/201015521COM 객체의 타입 변경 [1]
902임동찬8/16/201015642DISPPARAMS 관련 질문 [4]
901노력자8/11/201019461.net 2008 c# 에서 만든 com+ 는 com탭에서 사용불가..?! [3]파일 다운로드1
900박정환8/9/201017890wsHttpBinding을 WS-Addressing 없이 세팅할 수는 없을 까요? [7]
899임동찬7/30/201018170WCF heartbeat 관련 질문 [3]
898이영기7/29/201015927.NET 웹용에서 함수호출에러 [1]
897임동찬7/16/201017584Visual Studio 2008에서 DB연결 관련 문제 [1]
894최종문7/14/201016022try/finally 에 관해서 문의 드립니다.
895정성태7/15/201015714    답변글 [답변]: try/finally 에 관해서 문의 드립니다.
896최종문7/15/201016918        답변글 [답변]: [답변]: try/finally 에 관해서 문의 드립니다. [1]
893이병준 donator7/7/201018485Visual Studio 2010 에서의 Build 관련 질문 [1]
889임동찬7/6/201016195아래 887번에 대한 추가 질문
890정성태7/6/201017558    답변글 [답변]: 아래 887번에 대한 추가 질문
891임동찬7/6/201017452        답변글 [추가]: [답변]: 아래 887번에 대한 추가 질문
892정성태7/6/201014743            답변글 [답변]: [추가]: [답변]: 아래 887번에 대한 추가 질문
888김재영7/5/201014368TFS에 반드시! SQL Report랑 SharePoint(or WSS)가 붙어야 합니까? [2]
... 61  62  63  64  65  66  [67]  68  69  70  71  72  73  74  75  ...