성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[tree soap] 아차! f는 기억이 나는데, m은 ㅜㅜ 감사합니다!!! ^...
[정성태] 'm'은 decimal 타입의 숫자에 붙는 접미사입니다. ...
[정성태] https://lxr.sourceforge.io/ http...
[정성태] 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...
[정성태] 다시 질문을 정리할 필요가 있을 것 같습니다. 제가 본문에...
글쓰기
제목
이름
암호
전자우편
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'>C# - SynchronizationContext 기본 사용법</h1> <p> 그러고 보니, <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.threading.synchronizationcontext'>SynchronizationContext</a>에 관한 글을 쓴 적이 없군요. ^^; 단지, 아래의 Q&A에서 실질적인 이야기는 이미 했습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Dispatcher 와 synchronizationcontext의 관계가 궁금합니다. ; <a target='tab' href='https://www.sysnet.pe.kr/3/0/5262'>https://www.sysnet.pe.kr/3/0/5262</a> </pre> <br /> 가령 Windows Forms 같은 경우, 2차 스레드에서 <a target='tab' href='https://www.sysnet.pe.kr/2/0/11561'>UI를 생성한 스레드</a>로 작업을 맡기고 싶을 때가 있는데요, 이를 위해 Windows Forms는 <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.control.invoke'>Control.Invoke</a>/<a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.control.begininvoke'>BeginInvoke</a>를 제공합니다.<br /> <br /> 여기서 문제는, 라이브러리 개발자에게 있습니다. UI 스레드와 연동해야 할 공통 코드를 만들려고 하면 Windows Forms용과 WPF용으로 각각 나눠서 개발해야 하는 경우가 생깁니다. 왜냐하면, <a target='tab' href='https://www.sysnet.pe.kr/2/0/11561#control_invoke'>Control.Invoke/BeginInvoke 방식</a>은 Windows Forms에서만 제공할 뿐, <a target='tab' href='https://www.sysnet.pe.kr/2/0/747'>WPF는 Dispatcher.Invoke</a>로 바뀌었기 때문입니다.<br /> <br /> 이런 다양성에 대한 문제를 마이크로소프트는 SynchronizationContext로 해결합니다. 공통 라이브러리 개발자는 Control/Dispatcher에 상관없이 간단하게 SynchronizationContext.Post/Send를 이용해 대상 스레드에 작업을 맡길 수 있기 때문입니다.<br /> <br /> 결국, 각각의 환경은 단순히 SynchronizationContext를 공통적으로 제공하면 그만입니다. 일례로 Windows Forms는 기본적으로 UI를 생성하는 스레드에 <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.windowsformssynchronizationcontext'>WindowsFormsSynchronizationContext</a>가 설정돼 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // Windows Forms public partial class Form1 : Form { public Form1() { <span style='color: blue; font-weight: bold'>System.Diagnostics.Trace.WriteLine($"{SynchronizationContext.Current}");</span> // 출력 결과: System.Windows.Forms.WindowsFormsSynchronizationContext InitializeComponent(); } } </pre> <br /> 이와 마찬가지로 WPF, 심지어 .NET MAUI에서도 각각 그들만의 SynchronizationContext를 제공합니다.<br /> <br /> <ul> <li>WPF - <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.windows.threading.dispatchersynchronizationcontext'>DispatcherSynchronizationContext</a></li> <li>MAUI - DispatcherQueueSynchronizationContext</li> </ul> <br /> 사실 대부분의 SynchronizationContext가 UI 스레드에 작업을 맡기는 것과 밀접하게 연관돼 있어 단순히 UI 프레임워크와 연동되는 것으로 알고 계실 수도 있을 텐데 꼭 그런 것만은 아닙니다. 예를 들어, .NET Framework 시절의 ASP.NET 비동기에서도 AspNetSynchronizationContext가 제공되고 있으며 WCF나 심지어 Console Application에도 SynchronizationContext를 사용자가 만들어 제공하면 그만입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Console/Service Application을 위한 SynchronizationContext - AsyncContext ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12231'>https://www.sysnet.pe.kr/2/0/12231</a> </pre> <br /> 즉, 특정 스레드가 SynchronizationContext를 제공하는 측의 스레드에 작업을 맡기는 용도로 사용할 수 있는 것입니다. <br /> <br /> <hr style='width: 50%' /><br /> <br /> 간단하게 SynchronizationContext 사용 예를 들어볼까요? 가령, 2차 스레드에서 UI를 접근하려 할 때 Windows Forms의 경우 다음과 같은 식으로 코드를 자주 사용했을 것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public partial class Form1 : Form { public Form1() { } private void button1_Click(object sender, EventArgs e) { ThreadPool.QueueUserWorkItem((arg) => { <span style='color: blue; font-weight: bold'>button1.BeginInvoke</span>(() => { this.button1.Text = "TEST"; // UI 스레드에서 실행 }, null); }); } } </pre> <br /> 이것을 SynchronizationContext을 이용해 이렇게 바꾸는 것도 가능합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private void button1_Click(object sender, EventArgs e) { <span style='color: blue; font-weight: bold'>SynchronizationContext? ctx = SynchronizationContext.Current;</span> ThreadPool.QueueUserWorkItem((arg) => { // Control.BeginInvoke ==> SynchronizationContext.Post // Control.Invoke ==> SynchronizationContext.Send <span style='color: blue; font-weight: bold'>ctx?.Post</span>((arg1) => { this.button1.Text = "TEST"; // UI 스레드에서 실행 }, null); }); } </pre> <br /> 그러니까, 여러분들이 지금까지 사용한 코드 중에 SynchronizationContext를 사용해도 될 만한 코드가 저런 식으로 존재하긴 했지만, 단순히 Control.Invoke나 Dispatcher.Invoke 등으로 간단하게 처리할 수 있었기 때문에 굳이 필요성을 못 느꼈던 것입니다.<br /> <br /> 상황이 이렇기 때문에, 그다지 자주 쓰게 되는 타입은 아닙니다. 실제로 해당 타입은 .NET Framework 2.0부터 출현했지만 당시 이를 활용해 만들어진 타입은 기껏해야 <a target='tab' href='https://learn.microsoft.com/ko-kr/dotnet/api/system.componentmodel.backgroundworker'>BackgroundWorker</a> 정도에 불과했습니다. 이후 .NET Framework 4.0과 함께 <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task'>Task</a>가 등장했지만 그때도 여전히 SynchronizationContext가 수면 위로 떠오르지는 못했습니다.<br /> <a name='with_task_startnew'></a> <br /> Task의 경우 기존 코드 중 ThreadPool.QueueUserWorkItem을 대체하는 용도로 많이 사용되었는데요, 따라서 위에서 예를 들었던 코드는 Task로 이렇게 바뀔 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private void button1_Click(object sender, EventArgs e) { <span style='color: blue; font-weight: bold'>Task.Factory.StartNew</span>(() => { this.button1.Text = "TEST"; // UI 스레드에서 실행 }, CancellationToken.None, TaskCreationOptions.None, <span style='color: blue; font-weight: bold'>TaskScheduler.FromCurrentSynchronizationContext()</span>); } </pre> <br /> 그나마 이런 용도로 인해 SynchronizationContext가 눈에 들어오게 된 정도인데, 사실 이것조차도 여러분들은 다음과 같이 우회해서 사용했을 것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private void button1_Click(object sender, EventArgs e) { Task.Run(() => { <span style='color: blue; font-weight: bold'>button1.Invoke</span>(() => { this.button1.Text = "TEST"; }); }); } </pre> <br /> 제 개인적인 생각이지만, 아마도 Infragistics 등의 전문 UI 라이브러리 개발 업체를 제외하고는 실제 국내 개발자들의 일상적인 업무에서 SynchronizationContext를 직접 사용할 일은 거의 없었을 것입니다.<br /> <br /> 재미있게도, 이렇게 묻힐 것만 같았던 SynchronizationContext가 async/await이 나오면서 주목을 받게 됩니다. 이에 대한 이야기는 <a href='https://www.sysnet.pe.kr/2/0/13191'>다음 편</a>에서. ^^<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
2060
(왼쪽의 숫자를 입력해야 합니다.)