성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Java - How to use the Foreign Funct...
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
글쓰기
제목
이름
암호
전자우편
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# - Task.Yield 사용법</h1> <p> <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.yield?view=netcore-3.1'>Task.Yield</a>가 기존의 스레드에서 제공하던 Yield와,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Thread.Yield 메서드 ; <a target='tab' href='https://learn.microsoft.com/ko-kr/dotnet/api/system.threading.thread.yield'>https://learn.microsoft.com/ko-kr/dotnet/api/system.threading.thread.yield</a> </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;' > When would I use Task.Yield()? ; <a target='tab' href='https://stackoverflow.com/questions/22645024/when-would-i-use-task-yield'>https://stackoverflow.com/questions/22645024/when-would-i-use-task-yield</a> </pre> <br /> 새롭게 봤습니다. ^^<br /> <br /> <hr style='width: 50%' /><br /> <br /> <a target='tab' href='http://www.yes24.com/Cooperate/Naver/welcomeNaver.aspx?pageNo=1&goodsNo=82590356'>제가 쓴 책</a>을 보면 "10.2.4 Async 메서드가 아닌 경우의 비동기 처리" 절에서 "동기" 호출만 지원하는 메서드를 비동기로 제공하는 방법을 설명한 부분이 있습니다. 그게 Task.Yield의 기능을 설명하기 딱 좋은 사례일 듯합니다.<br /> <br /> 그러니까, File.ReadAllText와 같은 메서드의 경우 다음과 같이 동기 호출만 지원합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > static void OutputHOSTS() { string text = <span style='color: blue; font-weight: bold'>File.ReadAllText</span>(@"C:\Windows\system32\drivers\etc\HOSTS"); WriteLog(text); } </pre> <br /> 이 메서드를 비동기 호출로 변경하려면, 쉽게는 Task.Factory.StartNew의 힘을 빌려 다음과 같이 작성할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > static async Task OutputHOSTSAsync() { string text = <span style='color: blue; font-weight: bold'>await ReadAllTextAsync()</span>; WriteLog(text); } static Task<string> ReadAllTextAsync() { return <span style='color: blue; font-weight: bold'>Task.Factory.StartNew</span>( () => File.ReadAllText(@"C:\Windows\system32\drivers\etc\HOSTS") ); } </pre> <br /> 그런데, 이것보다 더 쉬운 방법이 바로 Task.Yield를 쓰는 것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > static async Task OutputHOSTSAsync() { string text = <span style='color: blue; font-weight: bold'>await ReadAllTextAsync2()</span>; WriteLog(text); } static async Task<string> ReadAllTextAsync2() { <span style='color: blue; font-weight: bold'>await Task.Yield();</span> return File.ReadAllText(@"C:\Windows\system32\drivers\etc\HOSTS"); } </pre> <br /> 어차피 중요한 것은, 스레드 풀의 스레드로 작업 실행을 넘기는 것이므로, 마이크로소프트는 그 역할을 Task.Yield 메서드에 구현해 두었고 우리는 저런 상황에서 사용해 주면 되는 것입니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 하지만, Task.Yield를 이용한 비동기 처리 방법은 SynchronizationContext가 마련된 환경에서는 효과가 없습니다. 왜냐하면 그 메서드가 SynchronizationContext를 고려하기 때문인데, "<a target='tab' href='https://stackoverflow.com/questions/22645024/when-would-i-use-task-yield'>When would I use Task.Yield()?</a>" 글의 덧글을 보면 이에 대해 Task.Yield의 내부 동작은 다음과 동일하다는 설명이 나옵니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > var tcs = new TaskCompletionSource<bool>(); <span style='color: blue; font-weight: bold'>var sc = SynchronizationContext.Current;</span> if (sc != null) <span style='color: blue; font-weight: bold'>sc.Post</span>(_ => tcs.SetResult(true), null); else <span style='color: blue; font-weight: bold'>ThreadPool.QueueUserWorkItem</span>(_ => tcs.SetResult(true)); await tcs.Task; </pre> <br /> 따라서, Windows Forms/WPF 등의 UI 스레드 환경에서 Task.Yield를 호출하게 되면 이후의 코드는 UI 스레드에서 작업하게 되고, 대부분의 경우 그것은 Task.Yield의 호출 유무에 영향을 받지 않게 됩니다. 일례로, 해당 글의 덧글에서 Task.Yield가 SynchronizationContext를 인식함으로 차이가 있다는 예제 코드를 자세히 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // 개인적으로, 해당 <a target='tab' href='https://stackoverflow.com/questions/22645024/when-would-i-use-task-yield'>질문/답변 글</a>의 문맥에서 봤을 때 이 예제는 잘못된 답변이라고 봅니다. private async void button_Click(object sender, EventArgs e) { <span style='color: blue; font-weight: bold'>await Task.Yield();</span> // Make us async right away var data = <span style='color: blue; font-weight: bold'>ExecuteFooOnUIThread()</span>; // This will run on the UI thread at some point later await UseDataAsync(); } Task UseDataAsync() { return Task.Factory.StartNew(() => { Debug.WriteLine("TEST"); }); } </pre> <br /> 어차피 Task.Yield가 없어도 - button_Click 이벤트의 실행 자체가 UI 스레드에서 실행되므로 ExecuteFooOnUIThread 메서드에 대해 Task.Yield의 호출이 있을 필요가 없습니다. 반면, 다음과 같이 스레드 풀의 스레드가 await 이후의 코드를 실행하는 환경에서는,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private async void button_Click(object sender, EventArgs e) { await UseDataAsync()<span style='color: blue; font-weight: bold'>.ConfigureAwait(false)</span>; // 이후의 코드는 SynchronizationContext.Current == null인 스레드 풀의 스레드에서 실행되므로, await Task.Yield(); // Task.Yield 내부에서는 어차피 UI 스레드로 되돌릴 수도 없어, var data = ExecuteFooOnUIThread(); // 이 코드는 스레드 풀의 스레드에서 실행 } </pre> <br /> Task.Yield는 이후의 코드를 UI 스레드에서 실행할 수 없으므로 마찬가지로 호출 여부가 크게 의미가 없습니다. 결국 SynchronizationContext.Current가 설정된 환경에서는 Task.Yield를 사용할 이유가 없는 것입니다. 실제로, 이 글의 처음 예제에서 만들어 둔 ReadAllTextAsync2 코드를 button_Click 이벤트에서 실행해 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private async void button_Click(object sender, EventArgs e) { string text = await ReadAllTextAsync2(); } static async Task<string> ReadAllTextAsync2() { await Task.Yield(); return File.ReadAllText(@"C:\Windows\system32\drivers\etc\HOSTS"); } </pre> <br /> button_Click 및 ReadAllTextAsync2의 호출은 UI 스레드에서 진행되고, Task.Yield는 SynchronizationContext.Current를 고려하므로 마찬가지로 그 이후의 File.ReadAllText 역시 UI 스레드에서 진행되어 비동기 호출이라는 장점이 없어집니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 개발자가 이런 사실을 적절히 잘 이해한다면, 특정 환경의 경우 Task.Yield는 기존 코드에서 스레드를 사용하던 부분에 대한 코드를 간단하게 만드는 용도라고 이해하는 것도 나쁘진 않습니다.<br /> <br /> 예를 들어 볼까요? 간혹 TCP 서버를 만들면서 접속한 클라이언트에 대해 하나의 스레드를 할당해 처리를 하는 경우가 있습니다.<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.Net; using System.Net.Sockets; using System.Threading; namespace TCPServer { class Program { static void Main(string[] args) { using (Socket srvSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) { IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 11200); srvSocket.Bind(endPoint); srvSocket.Listen(10); while (true) { Socket clntSocket = srvSocket.Accept(); <span style='color: blue; font-weight: bold'>Thread t = new Thread(processClientSocket);</span> t.IsBackground = true; <span style='color: blue; font-weight: bold'>t.Start(clntSocket);</span> } } } private static void <span style='color: blue; font-weight: bold'>processClientSocket</span>(object objSock) { Socket clntSocket = objSock as Socket; // ... 다른 스레드에서 실행 ... Console.WriteLine("new client"); clntSocket.Close(); } } } </pre> <br /> 이럴 때, Task.Yield를 사용해 기존 코드를 대체할 수 있습니다.<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.Net; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; namespace TCPServer { class Program { static async Task Main(string[] args) { using (Socket srvSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) { IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 11200); srvSocket.Bind(endPoint); srvSocket.Listen(10); while (true) { Socket clntSocket = srvSocket.Accept(); <span style='color: blue; font-weight: bold'>/* await */ processClientSocketAsync(clntSocket);</span> } } } static async Task processClientSocketAsync(Socket clntSocket) { <span style='color: blue; font-weight: bold'>await Task.Yield();</span> // ... 다른 스레드에서 실행 ... Console.WriteLine("new client"); clntSocket.Close(); } } } </pre> <br /> 그러니까, 일정 부분 이후의 기존 코드를 별도의 스레드에서 실행하기를 원한다면 Task.Yield로 간단하게 대체해 줄 수 있는 것입니다. (물론, SynchronizationContext가 구성된 환경에서는 단일 스레드로 동작하게 됩니다.)<br /> <br /> 이 정도면, Task.Yield에 대한 느낌이 대충 오시나요? ^^ <br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1594&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1810
(왼쪽의 숫자를 입력해야 합니다.)