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

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

1  2  3  4  5  6  7  [8]  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
5752푸헐11/15/20223418app.config 에 connectionStrings를 aspnet_regiis로 enctyption [4]
5751차가워11/8/20224215vs2022 preview net7 AOT 콘솔 실행 성능 [7]
5749차가워11/4/20223625전처리 지시문 #if DEBUG 배포시 실행 여부 [1]
5748김기헌11/3/20223949안녕하세요 선생님 싱글톤 패턴을 꼭 이렇게 사용해야 하나요? [6]
5747김기헌11/2/20223328안녕하세요 선생님 네트워크 관련 용어 중 IP 주소가 왜 논리적 주소라고 표현되는 건가요? [2]
5746물냉면이...11/2/20223512서로 다른 클래스에 있는 동일 함수의 일괄 호출 방법에 대해 궁금합니다. [3]
5745흰털너부리11/1/20223869.net core web api 사용 제한에 관한 질문 입니다. [2]
5744차가워10/31/20224254윈폼 Console.WriteLine(); 연산 문의 [1]
5743흰털너부리10/27/20223589reflection, static, override 질문입니다. [1]
5742차가워10/27/20223479하나의 socket에 여러 스레드가 접근 하는 경우 [1]
5741조호상10/27/20223739OpenCVSharp4 구현 가능 문의 [1]
5740혜성10/26/20224570Visual Studio 2022 C# 콘솔 프로그램 기본 코드 변경된 이유는 무엇인가요? [2]
5739슬픈단잠10/25/20223574조언 주신 방법으로 해봤으나, 여전히 어디가 문제인지 파악을 못했습니다. [2]
5738슬픈단잠10/24/20223979C# 마샬링 관련 질문입니다. [2]
5737감사합니...10/20/20224628찾은 어셈블리의 매니페스트 정의와 어셈블리 참조가 일치하지 않습니다. [8]파일 다운로드1
5736초보 개...10/19/20223637c# winforms 프로그램을 setup 파일로 배포 시, 설정 문의 [2]
5735농상10/17/20224272싱글톤 공부중 질문이 있습니다. [8]
5734mins10/14/20224019델파이 dll을 c#에서 사용하기 관련해서 포인트 관련이라 질문을 올립니다. [2]
5733김경환10/12/20224042선생님 질문하나만드리겠습니다. [1]
5732kss10/8/20224323c# socket.poll 버그인가요? [2]파일 다운로드1
5731kss10/8/20223884c# socket.poll 버그인가요? [3]
5730김재환10/7/20224380WPF에서 디스플레이 배율이 100%가 아닌 경우, Window의 포지션 정보가 부정확해지는 문제 [2]
5729김기헌10/2/20224060안녕하세요 선생님 뮤텍스 관련 질문 드립니다 [2]
5728김경환9/29/20224065그리드뷰관련마지막질문하나드리겠습니다선생님 [5]파일 다운로드1
5727김경환9/26/20223974c# 윈폼 tcp/ip 기반 데이터그리드뷰질문하나드리겟습니다 [3]
5726양승조9/22/20224698C# dll 과 C++ 간 배열 전달. SafeArray [10]파일 다운로드1
1  2  3  4  5  6  7  [8]  9  10  11  12  13  14  15  ...