성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
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# - 동기 방식이면서 비동기 메서드(awaitable)처럼 구현한 사례</h1> <p> 일반 메서드를 필요하다면 (awaitable) Async로 만드는 방법은 Task 객체를 반환하도록 하면 됩니다. 가령, 5초 동안 스레드를 중지하는 다음의 메서드에 대해,<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.Threading; namespace ConsoleApp1 { class Program { static void Main(string[] args) { new MyClass().Test(); } } class MyClass { public void Test() { <span style='color: blue; font-weight: bold'>Thread.Sleep(1000);</span> } } } </pre> <br /> TestAsync 메서드를 다음과 같이 만들어 줄 수 있습니다.<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; using System.Threading.Tasks; namespace ConsoleApp1 { class Program { static async Task Main(string[] args) { <span style='color: blue; font-weight: bold'>await new MyClass().TestAsync();</span> Console.WriteLine("End"); } } class MyClass { <span style='color: blue; font-weight: bold'>public Task TestAsync()</span> { <span style='color: blue; font-weight: bold'>Task t = new Task(() => Thread.Sleep(1000));</span> t.Start(); <span style='color: blue; font-weight: bold'>return t;</span> } } } </pre> <br /> 그런데, Task 객체를 (직접 명시적으로) 만들지 않으면서 Async 기능을 구현한 것처럼 만들 수도 있습니다. 이럴 때 TaskCompletionSource 객체를 사용하면 됩니다.<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; using System.Threading.Tasks; namespace ConsoleApp1 { class Program { static async Task Main(string[] args) { <span style='color: blue; font-weight: bold'>await new MyClass().TestAsync2();</span> Console.WriteLine("End: TestAsync2"); } } class MyClass { public Task TestAsync2() { <span style='color: blue; font-weight: bold'>TaskCompletionSource<object> src = new TaskCompletionSource<object>();</span> try { Console.WriteLine("TestAsync2 called!"); <span style='color: blue; font-weight: bold'>src.SetResult(null);</span> } catch (Exception e) { <span style='color: blue; font-weight: bold'>src.SetException(e);</span> } <span style='color: blue; font-weight: bold'>return src.Task;</span> } } } </pre> <br /> 뭐 저런 쓸모 없는 코드가 다 있나... 할 수 있습니다. 그런데 이런 쓸모없는 코드를 .NET BCL에서 찾아 볼 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // System.Data.dll // System.Data.Common.DbConnection public <span style='color: blue; font-weight: bold'>virtual Task OpenAsync</span>(CancellationToken cancellationToken) { <span style='color: blue; font-weight: bold'>TaskCompletionSource<object> source = new TaskCompletionSource<object>();</span> if (cancellationToken.IsCancellationRequested) { source.SetCanceled(); } else { try { <span style='color: blue; font-weight: bold'>this.Open();</span> <span style='color: blue; font-weight: bold'>source.SetResult(null);</span> } catch (Exception exception) { <span style='color: blue; font-weight: bold'>source.SetException(exception);</span> } } <span style='color: blue; font-weight: bold'>return source.Task;</span> } </pre> <br /> 위에서 보는 바와 같이 System.Data.Common.DbConnection은 OpenAsync 메서드를 제공하지만, 이름과는 달리 내부적으로 처리는 완전하게 동기로 하고 있습니다.<br /> <br /> 이런 바보 같은 코드가 왜 나왔을까요? 사실 DbConnection은 추상 클래스이기 때문에 기본적인 기능만 제공하는 정도입니다. 그리고 실질적인 기능들은 이를 상속받은 타입들이 하게 되는데, 자식 클래스들에게 비동기를 강제로(?) 구현하게 짐을 지우는 것이 어떤 경우에는 부담이 될 수 있습니다. 그래서, 인터페이스 측면에서 비동기 기능을 제공하는 가상 메서드는 제공하지만 동기처럼 동작하는 저런 구현이 나온 것입니다.<br /> <br /> 실제로 System.Data.SqlClient의 SqlConnection은 OpenAsync를 다시 구현하면서 비동기 기능을 완전하게 제공합니다.<br /> <br /> 그 외에도, 일정에 쫓겨 비동기 기능을 구현하진 못했지만 언젠가(?) 꼭 구현하리라 다짐하고 Async 메서드를 만들어 둔다면 저런 식으로 할 수 있을 것입니다. ^^<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1581
(왼쪽의 숫자를 입력해야 합니다.)