성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] VT sequences to "CONOUT$" vs. STD_O...
[정성태] NetCoreDbg is a managed code debugg...
[정성태] Evaluating tail call elimination in...
[정성태] What’s new in System.Text.Json in ....
[정성태] What's new in .NET 9: Cryptography ...
[정성태] 아... 제시해 주신 "https://akrzemi1.wordp...
[정성태] 다시 질문을 정리할 필요가 있을 것 같습니다. 제가 본문에...
[이승준] 완전히 잘못 짚었습니다. 댓글 지우고 싶네요. 검색을 해보...
[정성태] 우선 답글 감사합니다. ^^ 그런데, 사실 저 예제는 (g...
[이승준] 수정이 안되어서... byteArray는 BYTE* 타입입니다...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>(번역글) .NET Internals Cookbook Part 10 - Threads, Tasks, asynchronous code and others</h1> <p> 이번에도 .NET Internals Cookbook 시리즈의 10번째 글을 번역한 것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > .NET Internals Cookbook Part 10 - Threads, Tasks, asynchronous code and others ; <a target='tab' href='https://blog.adamfurmanek.pl/2019/04/20/net-internals-cookbook-part-10/'>https://blog.adamfurmanek.pl/2019/04/20/net-internals-cookbook-part-10/</a> </pre> <br /> <hr style='width: 50%' /><br /> <br /> <br /><div style='font-size: 12pt; font-family: Malgun Gothic, Consolas; color: #2211AA; text-align: left; font-weight: bold'>65. async void 유형의 메서드에서 발생한 예외 처리 방법</div> <br /> async 메서드는 반환 값이 없는 경우 Task, 있는 경우 Task<T>를 쓰는 것이 권장된다고 아래의 글에서 설명한 적이 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > async 메서드의 void 반환 타입 사용에 대하여 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11414'>http://www.sysnet.pe.kr/2/0/11414</a> </pre> <br /> async void의 경우 예외 처리를 하려면 별도의 synchronization context와 task scheduler를 정의해야 합니다. 이에 대해서는 다음의 글에서 잘 설명하고 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Async Wandering ; <a target='tab' href='https://blog.adamfurmanek.pl/blog/2018/10/06/async-wandering-part-5-catching-exceptions-from-async-void/'>https://blog.adamfurmanek.pl/blog/2018/10/06/async-wandering-part-5-catching-exceptions-from-async-void/</a> </pre> <br /> 물론 저 글을 읽고 자신만의 synchronization context와 task scheduler를 정의하는 것도 가능하겠지만 기왕이면 이미 만들어져있는 다음의 라이브러리를 쓰는 것도 좋겠습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > StephenCleary/AsyncEx ; <a target='tab' href='https://github.com/StephenCleary/AsyncEx'>https://github.com/StephenCleary/AsyncEx</a> </pre> <br /> <hr style='width: 50%' /><br /> <br /> <br /><div style='font-size: 12pt; font-family: Malgun Gothic, Consolas; color: #2211AA; text-align: left; font-weight: bold'>66. Stream은 다중 스레드 환경에 안전할까?</div> <br /> NOPE!<br /> <br /> <hr style='width: 50%' /><br /> <a name='67'></a> <br /> <br /><div style='font-size: 12pt; font-family: Malgun Gothic, Consolas; color: #2211AA; text-align: left; font-weight: bold'>67. Thread.Yield와 Thread.Sleep(0)의 차이점</div> <br /> Thread.Yield는 <a target='tab' href='https://learn.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-switchtothread'>SwitchToThread</a> Win32 API를 호출하는데, 이 함수는 이것을 실행하는 "현재 프로세서에 ready 상태의 스레드가 있는지 체크" 후 있으면 해당 스레드로 전환이 되지만 없으면 현재 스레드가 계속 실행되도록 합니다. 원 글(<a target='tab' href='https://blog.adamfurmanek.pl/2019/04/20/net-internals-cookbook-part-10/'>.NET Internals Cookbook Part 10 - Threads, Tasks, asynchronous code and others</a>)에서는 이와 함께 SwitchToThread API가 커널 모드로의 전환을 하지 않는다고 하는데... 이게 좀 맞지 않는 설명입니다. 커널 모드로 전환은 하지만, 다른 스레드로 문맥 전환(Context switching)이 발생하지 않을 수 있습니니다.<br /> <br /> <br /> (20222-04-18 업데이트) <a href='https://www.sysnet.pe.kr/2/0/13033'>https://www.sysnet.pe.kr/2/0/13033</a><br /> <br /> <strike>현재 프로세서에 우선순위에 상관없이 ready 상태의 스레드만 없다면 SwitchToThread API의 사용은 CPU 100% 점유를 할 수 있습니다.</strike><br /> <strike>반면, <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.threading.thread.sleep?view=netframework-4.8'>Thread.Sleep(0)</a>은 호출 시 무조건 자신에게 남겨진 퀀텀 시간을 포기하고 다음번 스케줄링으로 넘어갑니다. 따라서, ready 상태의 스레드가 다른 프로세스에라도 있기만 한다면 문맥 전환을 가져옵니다. 물론, 없다면 Thread.Sleep(0)을 호출한 스레드는 스케줄링에서 다시 선택되어져서 연이어 실행이 될 것입니다. 하지만 일반적인 스케줄링 정책을 따르는 것이기 때문에 SwitchToThread와는 달리 우선순위가 낮은 스레드에 양보하지는 않습니다. (윈도우는 우선순위가 낮은 스레드가 3~4초 정도 스케줄링에 밀리게 되는 경우 우선순위를 15로 임시 조절을 해 다음번 스케줄링에서 선택되게 합니다.)<br /> <br /> 또한 원 글(<a target='tab' href='https://blog.adamfurmanek.pl/2019/04/20/net-internals-cookbook-part-10/'>.NET Internals Cookbook Part 10 - Threads, Tasks, asynchronous code and others</a>)에서는 Thread.Sleep(0)의 경우 CPU 사용량을 0으로 낮출 수 없다고 하는데 어차피 SwitchToThread도 마찬가지이므로 굳이 이걸 언급한 이유를 모르겠습니다.</strike> <br /> <br /> 참고로, Thread.Sleep(1)은 최소 1ms를 대기하도록 하지만 아래의 글에 설명한 대로 상황에 따라 달라질 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 윈도우 운영체제의 시간 함수 (2) - Sleep 함수의 동작 방식 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11065'>http://www.sysnet.pe.kr/2/0/11065</a> </pre> <br /> <strike>그건 그렇긴 한데... Thread.Yield와 Thread.Sleep의 특성을 눈에 띄게 구분지을 만한 예제가 없습니다. 가령, 스레드 3개를 생성해 모두 CPU 친화성을 동일한 Processor로 설정해 둔 후 스레드 2개는 우선순위를 낮추고(설령 높여도) 다른 하나는 Yield만을 하는 스레드가 있다면,</strike><br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.Diagnostics; using System.Numerics; using System.Threading; namespace ConsoleApp1 { class Program { static int _processorId = 1; static void Main(string[] args) { Thread t1 = new Thread(yieldProc); Thread t2 = new Thread(lowPriorityProc); Thread t3 = new Thread(lowPriorityProc); t3.Start(); t2.Start(); t1.Start(); t1.Join(); t2.Join(); } private static void lowPriorityProc() { SetIdealProcessor(_processorId); Console.WriteLine("lowPriorityProc: " + AppDomain.GetCurrentThreadId()); Thread.CurrentThread.Priority = ThreadPriority.Lowest; int i = 0; BigInteger sum = new BigInteger(); while (true) { i++; sum += i; } } private static void yieldProc() { SetIdealProcessor(_processorId); Console.WriteLine("yieldProc: " + AppDomain.GetCurrentThreadId()); while (true) { Thread.Yield(); } } // <a target='tab' href='https://www.sysnet.pe.kr/2/0/10933'>https://www.sysnet.pe.kr/2/0/10933</a> static void SetIdealProcessor(int cpuNumber) { if (cpuNumber >= Environment.ProcessorCount) { cpuNumber = 0; } foreach (ProcessThread pthread in Process.GetCurrentProcess().Threads) { if (pthread.Id == AppDomain.GetCurrentThreadId()) { pthread.ProcessorAffinity = new IntPtr(1 << cpuNumber); break; } } } } } </pre> <br /> <strike>Yield 쪽 스레드는 CPU 100% 현상을 보이지 않아야 할 것 같은데 아닙니다. 현상을 놓고 가정해 보면 스레드 친화성을 지정했지만 실제로 실행해 보면 다중 코어가 함께 100% 현상으로 나오므로 다른 한쪽 스레드가 Yield를 해도 다른 스레드들이 이미 다른 코어로 옮겨진 상태여서 효과가 나지 않는 것 같습니다. (혹시 Yield, Sleep(0)에 대한 뚜렷한 특성을 테스트할 수 있는 예제를 알고 계신 분은 덧글 부탁드립니다. ^^)</strike><br /> <br /> <hr style='width: 50%' /><br /> <br /> <a name='qa68'></a> <br /><div style='font-size: 12pt; font-family: Malgun Gothic, Consolas; color: #2211AA; text-align: left; font-weight: bold'>68. .NET 응용 프로그램은 기본적으로 몇 개의 스레드가 생성될까?</div> <br /> 간단한 콘솔 프로그램의 경우,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; namespace Program { public class Program { public static void Main(string[] args) { Console.WriteLine("Attach WinDBG and check!"); Console.ReadLine(); } } } </pre> <br /> windbg로 다음과 같이 "~" 명령어로 총 스레드를 확인할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:006> <span style='color: blue; font-weight: bold'>~</span> 0 Id: 7180.6a60 Suspend: 1 Teb: 00a25000 Unfrozen 1 Id: 7180.7a88 Suspend: 1 Teb: 00a28000 Unfrozen 2 Id: 7180.5a1c Suspend: 1 Teb: 00a2b000 Unfrozen 3 Id: 7180.7c54 Suspend: 1 Teb: 00a2e000 Unfrozen 4 Id: 7180.7c28 Suspend: 1 Teb: 00a31000 Unfrozen 5 Id: 7180.3678 Suspend: 1 Teb: 00a34000 Unfrozen . 6 Id: 7180.7fa0 Suspend: 1 Teb: 00a37000 Unfrozen </pre> <br /> 표면상으로 보면 7개가 맞을 것 같지만 아닙니다. 왜냐하면 이 중에서 마지막의 6번 스레드는 windbg를 attach함으로써 생성된 디버그용 스레드이기 때문입니다. 실제로 6번 스레드의 콜 스택을 확인하면 다음과 같이 DbgUiRemoteBreakin인 것을 볼 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:006> <span style='color: blue; font-weight: bold'>k</span> # ChildEBP RetAddr 00 0178f7e4 77efb3a9 ntdll!DbgBreakPoint 01 0178f814 76860419 ntdll!DbgUiRemoteBreakin+0x39 02 0178f824 77eb662d KERNEL32!BaseThreadInitThunk+0x19 03 0178f880 77eb65fd ntdll!__RtlUserThreadStart+0x2f 04 0178f890 00000000 ntdll!_RtlUserThreadStart+0x1b </pre> <br /> 따라서 최소 생성되는 스레드 수는 6개입니다. 그리고 이 중에서 Managed 스레드의 수를 SOS.dll 확장 명령어로 볼 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:006> <span style='color: blue; font-weight: bold'>!threads</span> ThreadCount: 2 UnstartedThread: 0 BackgroundThread: 1 PendingThread: 0 DeadThread: 0 Hosted Runtime: no Lock ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception 0 1 6a60 00eaa6c8 2a020 Preemptive 02C0461C:00000000 00e9d598 1 MTA 5 2 3678 00eba478 2b220 Preemptive 00000000:00000000 00e9d598 0 MTA (Finalizer) </pre> <br /> 정리하면 다음과 같이 구분이 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0번: Main 함수를 실행하는 스레드 5번: Finalizer를 실행하는 스레드 1, 2, 3, 4번: ThreadPool의 최소 스레드 6번: Windbg의 Debug Thread </pre> <br /> 여기서 ThreadPool의 최소 스레드 수가 4인데요, 실제로 이것은 다음의 닷넷 코드로 확인할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > ThreadPool.GetMinThreads(out int workerThreads, out int completionPortThreads); Console.WriteLine(workerThreads); // 출력 결과: 4 Console.WriteLine(completionPortThreads); // 출력 결과: 4 </pre> <br /> 이를 기반으로 예제를 다음과 같이 재구성할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.Diagnostics; using System.Threading; namespace Program { public class Program { public static void Main(string[] args) { ThreadPool.GetMinThreads(out int workerThreads, out int completionPortThreads); Console.WriteLine($"workerThreads: {workerThreads}"); OutputThreads(); Console.WriteLine("Attach WinDBG and check!"); Console.ReadLine(); } static void OutputThreads() { int id = AppDomain.GetCurrentThreadId(); Console.WriteLine(); int n = 0; foreach (ProcessThread thread in Process.GetCurrentProcess().Threads) { string threadType = "(ThreadPool)"; if (id == thread.Id) { threadType = "(Managed)"; } if (thread.BasePriority == 10) { threadType = "(Finalizer)"; } Console.WriteLine($"[{n}] {thread.Id}, (0x" + thread.Id.ToString("x") + $") {threadType}"); n++; } } } } /* 출력 결과 workerThreads: 4 [0] 31708, (0x7bdc) (Managed) [1] 25456, (0x6370) (ThreadPool) [2] 19776, (0x4d40) (ThreadPool) [3] 28448, (0x6f20) (ThreadPool) [4] 29228, (0x722c) (ThreadPool) [5] 30808, (0x7858) (Finalizer) Attach WinDBG and check! */ </pre> <br /> 그런데 간혹 ThreadPool의 스레드 수가 3으로 출력되는 경우를 볼 수 있습니다. 아마도 스레드 풀의 스레드 생성에 대한 지연 시간이 있는 듯도 한데 정확하진 않습니다.<br /> <br /> 참고로 다음의 글도 한번 읽어보시고. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > .NET 응용 프로그램에 기본 생성되는 스레드들에 대한 탐구 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1247'>http://www.sysnet.pe.kr/2/0/1247</a> </pre> <br /> <hr style='width: 50%' /><br /> <a name='69'></a> <br /> <br /><div style='font-size: 12pt; font-family: Malgun Gothic, Consolas; color: #2211AA; text-align: left; font-weight: bold'>69. 스레드가 차지하는 기본 메모리?</div> <br /> 스레드의 생성으로 사용자 공간에서는 1MB의 스택 크기가 소비되고, 커널 공간에는 12kB(x86) 또는 24kB(x64)가 소비됩니다. 그 외, 만약 64비트운영체제에서 실행되는 32비트 프로세스라면 WoW64 계층으로 별도의 스택 공간이 사용됩니다.<br /> <br /> 다음의 글을 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Increasing the Size of your Stack (.NET Memory Management: Part 3) ; <a target='tab' href='http://content.atalasoft.com/atalasoft-blog/increasing-the-size-of-your-stack-net-memory-management-part-3'>http://content.atalasoft.com/atalasoft-blog/increasing-the-size-of-your-stack-net-memory-management-part-3</a> </pre> <br /> 기본 스택 크기를 (.NET 2.0부터 지원되는) Thread 생성자의 2번째 인자를 지정해 변경하는 방법이 나옵니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Thread t = new Thread(threadFunc, 1024 * 1024); // 기본 1MB, 또는 0으로 지정 // 최소 262,144(256kB) </pre> <br /> 또는, editbin을 이용해 기본 크기를 어셈블리 파일 생성 후 PE 헤더를 변경하는 것으로도 지정할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > EDITBIN.EXE /STACK:524288 file.exe </pre> <br /> <hr style='width: 50%' /><br /> <br /> <br /><div style='font-size: 12pt; font-family: Malgun Gothic, Consolas; color: #2211AA; text-align: left; font-weight: bold'>70. 예외가 발생하는 async Task 메서드를 await으로 호출하지 않는다면?</div> <br /> async Task 메서드에 발생한 예외는 await 호출을 하지 않은 응용 프로그램에는 전달되지 않습니다. 대신, TaskScheduler.UnobservedTaskException 이벤트를 구독하는 경우 Task에서 발생한 예외를 보관한 TaskExceptionHolder 객체가 GC되는 시점에 Finalizer의 호출에 의해 예외를 검출할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.Threading.Tasks; namespace Program { class Program { static void Main(string[] args) { TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; Test(); Console.ReadLine(); GC.Collect(); GC.WaitForPendingFinalizers(); } static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) { Console.WriteLine("Unobserved exception!"); Console.WriteLine(e.Exception); } public static async Task Test() { throw new Exception(); } } } </pre> <br /> 참고로 TaskScheduler_UnobservedTaskException 발생 시점의 call-stack은 다음과 같습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > ConsoleApp1.exe!Program.Program.TaskScheduler_UnobservedTaskException(object sender, System.Threading.Tasks.UnobservedTaskExceptionEventArgs e) Line 20 C# mscorlib.dll!System.Threading.Tasks.TaskScheduler.PublishUnobservedTaskException(object sender, System.Threading.Tasks.UnobservedTaskExceptionEventArgs ueea) Line 521 C# > mscorlib.dll!System.Threading.Tasks.TaskExceptionHolder.~TaskExceptionHolder() Line 145 C# </pre> <br /> <hr style='width: 50%' /><br /> <br /> <br /><div style='font-size: 12pt; font-family: Malgun Gothic, Consolas; color: #2211AA; text-align: left; font-weight: bold'>71. CLR이 fiber를 지원할까?</div> <br /> 저도 이에 대해 전에 글을 쓴 적이 있는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > ManagedThreadId - 두 번째 이야기 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/492'>http://www.sysnet.pe.kr/2/0/492</a> </pre> <br /> 공식적으로도 현재 지원을 안 하고 있지만, .NET 2.0에서 fiber 지원을 하려다 포기했는데 4.8까지 오는 동안 이 부분으로 전혀 소식이 없는 것을 보면 향후에도 매우 어려울 것으로 보입니다.<br /> <br /> <hr style='width: 50%' /><br /> <a name='72'></a> <br /> <br /><div style='font-size: 12pt; font-family: Malgun Gothic, Consolas; color: #2211AA; text-align: left; font-weight: bold'>72. Thread.Yield나 Thread.Join은 COM 메시지를 처리할까?</div> <br /> 다음의 글에 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Which blocking operations cause an STA thread to pump COM messages? ; <a target='tab' href='https://stackoverflow.com/questions/21571598/which-blocking-operations-cause-an-sta-thread-to-pump-com-messages'>https://stackoverflow.com/questions/21571598/which-blocking-operations-cause-an-sta-thread-to-pump-com-messages</a> </pre> <br /> 다음의 동작들에서 Win32 메시지 처리가 된다고 합니다.<br /> <br /> <ul> <li>Thread.Join</li> <li>WaitHandle.WaitOne/WaitAny/WaitAll</li> <li>GC.WaitForPendingFinalizers</li> <li>Monitor.Enter</li> <li>ReaderWriterLock</li> <li>BlockingCollection</li> </ul> <br /> 보는 바와 같이 일단 Thread.Sleep이나 Thread.Yield에서는 메시지 처리가 없습니다. 하지만 위의 목록에 있다 해도 모든 종류의 Win32 메시지가 처리된다는 것은 아니므로 COM 메시지가 처리되는지는 꼭 테스트를 해보는 것이 좋습니다.<br /> <br /> 참고로, COM 메시지 처리에 메시지 루프를 이용하는 것이 가장 확실합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# COM 서버에 구독한 COM 이벤트를 C++에서 받는 방법 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11679'>http://www.sysnet.pe.kr/2/0/11679</a> C# - 작업자 스레드와 UI 스레드 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11287'>http://www.sysnet.pe.kr/2/0/11287</a> </pre> <br /> <hr style='width: 50%' /><br /> <a name='73'></a> <br /> <br /><div style='font-size: 12pt; font-family: Malgun Gothic, Consolas; color: #2211AA; text-align: left; font-weight: bold'>73. Thread.Abort의 동작 원리</div> <br /> Abort 호출로 인한 동작은 다음과 같습니다.<br /> <br /> <ol> <li>OS 스레드를 (종료가 아닌 일시) 중지,</li> <li>중지된 스레드의 관리 객체에 Abort를 요구한다는 메타데이터 비트를 설정,</li> <li>APC 함수를 APC 큐에 추가하고 스레드 실행을 재개</li> <li>스레드가 동작하면서 APC 큐에 추가된 함수를 실행할 수 있는 "alertable" 상태로 진입하기를 기대, 해당 APC 함수가 실행되면 메타데이터 비트가 설정되었는지 확인하고 예외를 발생</li> <li>만약 일정 시간 내에 alertable 상태로 진입하지 않으면 강제로 IP 레지스터의 값을 변경해 스레드의 실행을 가로채서 처리</li> </ol> <br /> 다음의 글에 쓰인 Thread.Abort에 대한 설명도 참고하시고.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Special Exceptions In .NET And How To Prepare Them ; <a target='tab' href='https://translate.google.com/translate?hl=en&sl=auto&tl=en&u=http%3A%2F%2Fchepa.net%2Fall%2F2018%2F10%2F11%2F%D0%BE%D1%81%D0%BE%D0%B1%D1%8B%D0%B5-%D0%B8%D1%81%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D1%8F-%D0%B2-net-%D0%B8-%D0%BA%D0%B0%D0%BA-%D0%B8%D1%85-%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%B8%D1%82%D1%8C%2F'>https://translate.google.com/translate?hl=en&sl=auto&tl=en&u=http%3A%2F%2Fchepa.net%2Fall%2F2018%2F10%2F11%2F%D0%BE%D1%81%D0%BE%D0%B1%D1%8B%D0%B5-%D0%B8%D1%81%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D1%8F-%D0%B2-net-%D0%B8-%D0%BA%D0%B0%D0%BA-%D0%B8%D1%85-%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%B8%D1%82%D1%8C%2F</a> 원문 - Особые исключения в .NET и как их готовить ; <a target='tab' href='https://habr.com/ru/company/jugru/blog/426045/'>https://habr.com/ru/company/jugru/blog/426045/</a> </pre> <br /> <hr style='width: 50%' /><br /> <br /> <br /><div style='font-size: 12pt; font-family: Malgun Gothic, Consolas; color: #2211AA; text-align: left; font-weight: bold'>74. CLR 메모리 모델(Memory model)</div> <br /> 다음의 글이 좋은 참고가 될 것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Memory Model ; <a target='tab' href='https://learn.microsoft.com/en-us/archive/blogs/cbrumme/memory-model'>https://learn.microsoft.com/en-us/archive/blogs/cbrumme/memory-model</a> CLR Memory Model ; <a target='tab' href='https://learn.microsoft.com/en-us/archive/blogs/jaredpar/clr-memory-model'>https://learn.microsoft.com/en-us/archive/blogs/jaredpar/clr-memory-model</a> CLR 2.0 Memory Model ; <a target='tab' href='http://joeduffyblog.com/2007/11/10/clr-20-memory-model/'>http://joeduffyblog.com/2007/11/10/clr-20-memory-model/</a> Memory Barriers in .NET ; <a target='tab' href='https://afana.me/archive/2015/07/10/memory-barriers-in-dot-net.aspx/'>https://afana.me/archive/2015/07/10/memory-barriers-in-dot-net.aspx/</a> </pre> <br /> 이글에서는 ECMA 메모리 모델과 CLR 2.0 메모리 모델의 규칙을 간단하게 살펴 봅니다.<br /> <br /> <br /><div style='font-size: 12pt; font-family: Malgun Gothic, Consolas; color: #2211AA; text-align: left; font-weight: bold'>ECMA 메모리 모델</div> <br /> <ol> <li>모든 기본형은 정렬되지만(short의 경우 2바이트 정렬, int32/float32의 경우 4바이트, int64/float64는 하드웨어 아키텍처에 따라 4/8바이트) "<a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.reflection.emit.opcodes.unaligned?view=netframework-4.8'>unaligned</a>" 명령어를 사용하면 정렬을 바꿀 수 있다.</li> <li>바이트 순서(Byte ordering)는 하드웨어 아키텍처에 의존</li> <li>런타임은 스레드에서 발생하는 모든 side effect와 예외가 CIL 명령어 순서로 실행되는 것을 보장</li> <li>native int 크기 내에서는 atomic하게 데이터가 이동</li> <li>volatile read는 acquire semantics</li> <li>volatile write는 release semantics</li> </ol> <br /> 위의 글에서 acquire semantics와 release semantics에 대해서는 다음의 글을 참고하시면 좋습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C/C++ volatile 키워드 ; <a target='tab' href='https://skyul.tistory.com/337'>https://skyul.tistory.com/337</a> [C++] Volatile 키워드 ; <a target='tab' href='https://aram88.tistory.com/entry/C-Volatile-%ED%82%A4%EC%9B%8C%EB%93%9C'>https://aram88.tistory.com/entry/C-Volatile-%ED%82%A4%EC%9B%8C%EB%93%9C</a> </pre> <br /> <br /><div style='font-size: 12pt; font-family: Malgun Gothic, Consolas; color: #2211AA; text-align: left; font-weight: bold'>CLR 2.0 메모리 모델(<a target='tab' href='http://joeduffyblog.com/2007/11/10/clr-20-memory-model/'>http://joeduffyblog.com/2007/11/10/clr-20-memory-model/</a>)</div> <br /> <ol> <li>로드/저장 간의 데이터 의존성은 보존된다. (Data dependence among loads and stores is never violated.)</li> <li>모든 저장은 release semantics <li>모든 volatile load는 acquire semantics <li>로드/저장은 barrier(예: Thread.MemoryBarrier, lock 획득, Interlocked.Exchange, Interlocked.CompareExchange,... 등)를 넘어 재정렬 될 수 없다.</li> <li>Loads and stores to the heap may never be introduced.</li> <li>로드/저장은 같은 위치로의 근접한 로드나 저장 시 합쳐져서 삭제될 수 있다.(Loads and stores may only be deleted when coalescing adjacent loads and stores from/to the same location)</li> </ol> <br /> 위의 내용은 다음의 글에 대한 요약이라고 하니,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [CHM] MSDN Magazine 2005년 10월 - Understand the Impact of Low-Lock Techniques in Multithreaded Apps ; <a target='tab' href='http://download.microsoft.com/download/3/a/7/3a7fa450-1f33-41f7-9e6d-3aa95b5a6aea/MSDNMagazineOctober2005en-us.chm'>http://download.microsoft.com/download/3/a/7/3a7fa450-1f33-41f7-9e6d-3aa95b5a6aea/MSDNMagazineOctober2005en-us.chm</a> </pre> <br /> 세부적인 의미는 CHM 파일에 있는 글을 읽어보는 것이 도움이 될 것입니다. 이외에도 MSDN Magazine 2012년 12월, 2013년 1월의 기사를 참고하면 좋습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - The C# Memory Model in Theory and Practice ; <a target='tab' href='https://learn.microsoft.com/en-us/archive/msdn-magazine/2012/december/csharp-the-csharp-memory-model-in-theory-and-practice'>https://learn.microsoft.com/en-us/archive/msdn-magazine/2012/december/csharp-the-csharp-memory-model-in-theory-and-practice</a> C# - The C# Memory Model in Theory and Practice, Part 2 ; <a target='tab' href='https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/january/csharp-the-csharp-memory-model-in-theory-and-practice-part-2'>https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/january/csharp-the-csharp-memory-model-in-theory-and-practice-part-2</a> </pre> <br /> 저도 한번 언제 마음 먹고 이 내용을 정리해야 하는데... 차일피일 미루기만 하는군요. ^^;<br /> </h1><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
8030
(왼쪽의 숫자를 입력해야 합니다.)