Microsoft MVP성태의 닷넷 이야기
.NET Framework: 347. C# - 프로세스(EXE) 수준의 Singleton 개체 생성 [링크 복사], [링크+제목 복사],
조회: 20508
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 2개 있습니다.)

C# - 프로세스(EXE) 수준의 Singleton 개체 생성

운영체제에 의해서 프로세스 격리가 제공되는 것을, 닷넷은 다시 프로세스 내부를 AppDomain이라는 단위로 격리시키고 서로 간의 직접 접근이 불가능하도록 만들었습니다.

그래서, Native 언어(예: C/C++)에서는 static으로 singleton을 만들었던 것을 닷넷에서는 static으로 지정했다고 프로세스 전역적으로 한 개만 존재하지는 않습니다, AppDomain 별로 존재하는 것이지.

이에 대한 해결책을 다음의 글에서 제공하고 있습니다.

Cross AppDomain Singleton 
; http://ingebrigtsen.info/2007/05/18/cross-appdomain-singleton/

근데, 코드가 별로 마음에 안 듭니다. ^^ Interop DLL이 별도로 추가된다는 것은 관리적인 요소만 하나 더 늘리게 되는 요인이 됩니다. 그래서, 아래의 방법을 곁들이면 더 좋습니다. ^^

레지스트리 등록 및 Interop DLL 없이 COM 개체 사용하는 방법
; https://www.sysnet.pe.kr/2/0/1180

public class CrossAppDomainSingleton<T> : MarshalByRefObject where T : new()
{
	...[생략]...
    private static AppDomain GetAppDomain(string friendlyName)
    {
        IntPtr enumHandle = IntPtr.Zero;
        ICorRuntimeHost host = null;

        try
        {
            host = Utility.CoCreateInstance("{CB2F6723-AB3A-11D2-9C40-00C04FA30A3E}") as ICorRuntimeHost;

            if (host == null)
            {
                return null;
            }

            host.EnumDomains(out enumHandle);

            object domain = null;
            while (true)
            {
                host.NextDomain(enumHandle, out domain);
                if (domain == null)
                {
                    break;
                }
                AppDomain appDomain = (AppDomain)domain;
                if (appDomain.FriendlyName.Equals(friendlyName))
                {
                    return appDomain;
                }
            }
        }
        finally
        {
            host.CloseEnum(enumHandle);
            Marshal.ReleaseComObject(host);
            host = null;
        }
        return null;
    }

	...[생략]...
}

지면(?) 관계상 ICorRuntimeHost 등의 구현은 이 글의 첨부 파일로 참고하시면 됩니다.

그나저나, 어쩔 수 없이 프로세스 수준의 Singleton을 사용해야 하지만 성능 손실은 과연 어느 정도일까요? 이를 위해 다음과 같이 테스트 코드를 작성해 봤습니다.

public static class NonSingleton
{
    static bool _test = true;
    public static bool Test { get { return _test; } }
}

public class Singleton : CrossAppDomainSingleton<Singleton>
{
    bool _test = true;
    public bool Test { get { return _test; } }
}

class Program
{
    static void Main(string[] args)
    {
        CheckTime(false, 1); // JIT 컴파일 시간을 배제하기 위해.
        CheckTime(true, 1000000);
    }

    private static void CheckTime(bool outputResult, int loopCount)
    {
        Stopwatch st = new Stopwatch();
        st.Start();

        int inc = 0;
        for (int i = 0; i < loopCount; i++)
        {
            if (NonSingleton.Test == true)
            {
                inc++;
            }
        }
        st.Stop();

        if (outputResult == true)
        {
            Console.WriteLine(st.ElapsedMilliseconds);
        }

        System.Diagnostics.Trace.WriteLine(inc);

        st.Start();

        inc = 0;
        Singleton singleton = Singleton.Instance;
        for (int i = 0; i < loopCount; i++)
        {
            if (singleton.Test == true)
            {
                inc++;
            }
        }

        st.Stop();

        if (outputResult == true)
        {
            Console.WriteLine(st.ElapsedMilliseconds);
        }

        System.Diagnostics.Trace.WriteLine(inc);
    }
}

Release 빌드 후 테스트 결과는 다음과 같이 나왔습니다.

10,000 번 호출 NonSingleton: 0ms
10,000 번 호출 Singleton: 4ms

100,000 번 호출 NonSingleton: 0ms
100,000 번 호출 Singleton: 45ms

1,000,000 번 호출 NonSingleton: 1ms
1,000,000 번 호출 Singleton: 454ms

10,000,000 번 호출 NonSingleton: 19ms
10,000,000 번 호출 Singleton: 4539ms

거의 200배가 넘게 느린 결과를 볼 수 있는데요. 무척 느리다 싶지만 이런 유의 테스트가 현실로 왔을 때는 재미있게 해석될 수 있습니다. 예를 들어, 1초에 10,000번 호출된다고 가정했을 때 도메인간 호출이 겨우 4ms 더 시간이 걸릴 뿐이므로 그다지 나쁜 결과는 아니라는 것입니다.

** 주의할 점은, 이는 호출 간의 속도 저하일 뿐 마샬링이 복잡해지면 더욱 느려질 수 있다는 사실!!!




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 2/27/2024]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2012-12-06 04시26분
[ryujh] 안녕하세요.

프로세스를 중복실행 방지하기 위해 뮤텍스를 사용하는데 이것이 Singleton 으로 봐도 될지요?

위의 예제는 해본적이 없어서 좀더 보고 질문드리겠습니다.
[guest]
2012-12-06 04시47분
개체가 하나면 싱글턴 개체라고 하는 것처럼, 프로세스가 하나라면 싱글턴 프로세스라는 말을 써도 되겠지요. 싱글턴 프로세스라면 위의 기법을 사용하기보다는 말씀하신 것처럼 뮤텍스가 올바른 선택일 수 있습니다.
정성태

... 16  17  [18]  19  20  21  22  23  24  25  26  27  28  29  30  ...
NoWriterDateCnt.TitleFile(s)
13216정성태1/12/20235497디버깅 기술: 184. windbg - 32비트 프로세스의 메모리 덤프인 경우 !peb 명령어로 나타나지 않는 환경 변수
13215정성태1/11/20237267Linux: 56. 리눅스 - /proc/pid/stat 정보를 이용해 프로세스의 CPU 사용량 구하는 방법 [1]
13214정성태1/10/20236482.NET Framework: 2087. .NET 6부터 SourceGenerator와 통합된 System.Text.Json [1]파일 다운로드1
13213정성태1/9/20235917오류 유형: 836. docker 이미지 빌드 시 "RUN apt install ..." 명령어가 실패하는 이유
13212정성태1/8/20235868기타: 85. 단정도/배정도 부동 소수점의 정밀도(Precision)에 따른 형변환 손실
13211정성태1/6/20235865웹: 42. (https가 아닌) http 다운로드를 막는 웹 브라우저
13210정성태1/5/20234958Windows: 219. 윈도우 x64의 경우 0x00000000`7ffe0000 아래의 주소는 왜 사용하지 않을까요?
13209정성태1/4/20234868Windows: 218. 왜 윈도우에서 가상 메모리 공간은 64KB 정렬이 된 걸까요?
13208정성태1/3/20234892.NET Framework: 2086. C# - Windows 운영체제의 2MB Large 페이지 크기 할당 방법파일 다운로드1
13207정성태12/26/20225160.NET Framework: 2085. C# - gpedit.msc의 "User Rights Assignment" 특권을 코드로 설정/해제하는 방법파일 다운로드1
13206정성태12/24/20225332.NET Framework: 2084. C# - GetTokenInformation으로 사용자 SID(Security identifiers) 구하는 방법 [3]파일 다운로드1
13205정성태12/24/20225577.NET Framework: 2083. C# - C++과의 연동을 위한 구조체의 fixed 배열 필드 사용 (2)파일 다운로드1
13204정성태12/22/20225037.NET Framework: 2082. C# - (LSA_UNICODE_STRING 예제로) CustomMarshaler 사용법파일 다운로드1
13203정성태12/22/20225261.NET Framework: 2081. C# Interop 예제 - (LSA_UNICODE_STRING 예제로) 구조체를 C++에 전달하는 방법파일 다운로드1
13202정성태12/21/20225639기타: 84. 직렬화로 설명하는 Little/Big Endian파일 다운로드1
13201정성태12/20/20226239오류 유형: 835. PyCharm 사용 시 C 드라이브 용량 부족
13200정성태12/19/20225107오류 유형: 834. 이벤트 로그 - SSL Certificate Settings created by an admin process for endpoint
13199정성태12/19/20225267개발 환경 구성: 656. Internal Network 유형의 스위치로 공유한 Hyper-V의 VM과 호스트가 통신이 안 되는 경우
13198정성태12/18/20225213.NET Framework: 2080. C# - Microsoft.XmlSerializer.Generator 처리 없이 XmlSerializer 생성자를 예외 없이 사용하고 싶다면?파일 다운로드1
13197정성태12/17/20224974.NET Framework: 2079. .NET Core/5+ 환경에서 XmlSerializer 사용 시 System.IO.FileNotFoundException 예외 발생하는 경우파일 다운로드1
13196정성태12/16/20225239.NET Framework: 2078. .NET Core/5+를 위한 SGen(Microsoft.XmlSerializer.Generator) 사용법
13195정성태12/15/20225768개발 환경 구성: 655. docker - bridge 네트워크 모드에서 컨테이너 간 통신 시 --link 옵션 권장 이유
13194정성태12/14/20225924오류 유형: 833. warning C4747: Calling managed 'DllMain': Managed code may not be run under loader lock파일 다운로드1
13193정성태12/14/20225929오류 유형: 832. error C7681: two-phase name lookup is not supported for C++/CLI or C++/CX; use /Zc:twoPhase-
13192정성태12/13/20226015Linux: 55. 리눅스 - bash shell에서 실수 연산
13191정성태12/11/20227279.NET Framework: 2077. C# - 직접 만들어 보는 SynchronizationContext파일 다운로드1
... 16  17  [18]  19  20  21  22  23  24  25  26  27  28  29  30  ...