성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 그냥 RSS Reader 기능과 약간의 UI 편의성 때문에 사용...
[이종효] 오래된 소프트웨어는 보안 위협이 되기도 합니다. 혹시 어떤 기능...
[정성태] @Keystroke IEEE의 문서를 소개해 주시다니... +_...
[손민수 (Keystroke)] 괜히 듀얼채널 구성할 때 한번에 같은 제품 사라고 하는 것이 아...
[정성태] 전각(Full-width)/반각(Half-width) 기능을 토...
[정성태] Vector에 대한 내용은 없습니다. Vector가 닷넷 BCL...
[orion] 글 읽고 찾아보니 디자인 타임에는 InitializeCompon...
[orion] 연휴 전에 재현 프로젝트 올리자 생각해 놓고 여의치 않아서 못 ...
[정성태] 아래의 글에 정리했으니 참고하세요. C# - Typed D...
[정성태] 간단한 재현 프로젝트라도 있을까요? 저런 식으로 설명만 해...
글쓰기
제목
이름
암호
전자우편
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# - 비동기 메서드의 async 예약어 유무에 따른 차이</h1> <p> 현실적으로 await 예약어를 사용해 호출할 수 있는 대상 메서드는 Task를 반환해야 합니다. 단지, 상황에 따라 async 예약어를 곁들일 수 있는데요, 아래의 예제 코드를 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > namespace ConsoleApp1; internal class Program { static async Task Main(string[] args) { DateTime now = DateTime.Now; <span style='color: blue; font-weight: bold'>await DelayTask(); await DelayAsync();</span> Console.WriteLine($"{(DateTime.Now - now).TotalSeconds}"); // 4초 소요 } static <span style='color: blue; font-weight: bold'>Task DelayTask</span>() { <span style='color: blue; font-weight: bold'>return Task.Delay(1000)</span>; } static <span style='color: blue; font-weight: bold'>async Task DelayAsync</span>() { <span style='color: blue; font-weight: bold'>await Task.Delay(3000)</span>; } } </pre> <br /> 결국 await 호출을 하는 측의 입장에서 보면 지정한 시간 동안 대기하는 동작은 똑같지만, DelayTask와 DelayAsync 메서드의 내부 코드에는 살짝(?) 다른 점이 존재합니다. 실제로 위의 예제 코드는 C# 컴파일러에 의해 각각 아래와 같이 처리됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private static Task DelayTask() // 변화가 없지만, { return Task.Delay(2000); } [DebuggerStepThrough] private static Task DelayAsync() // async 예약어로 인해 내부 코드를 상태 머신을 활용하는 코드로 변경 { Program.<DelayAsync>d__2 <DelayAsync>d__ = new Program.<DelayAsync>d__2(); <DelayAsync>d__.<>t__builder = AsyncTaskMethodBuilder.Create(); <DelayAsync>d__.<>1__state = -1; <DelayAsync>d__.<>t__builder.Start<Program.<DelayAsync>d__2>(ref <DelayAsync>d__); return <DelayAsync>d__.<>t__builder.Task; } </pre> <br /> 게다가 DelayAsync의 경우 위의 코드뿐만 아니라 내부 코드를 분할해 실행하는 상태 머신을 구현한 클래스를 별도로 하나 더 생성하기 때문에,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [CompilerGenerated] private sealed class <DelayAsync>d__2 : IAsyncStateMachine { // ...[생략]... void IAsyncStateMachine.MoveNext() { // ...[생략]... } [DebuggerHidden] void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine) { } } </pre> <br /> 복잡도가 더 증가합니다. 단순히 생각해 봐도, 어차피 같은 역할을 하는 것이라면 async를 적용하지 않는 편이 더 깔끔하고 성능상으로도 (작게나마) 이득을 볼 수 있습니다. 그렇다면 async를 붙이는 적절한 기준이 있을까요?<br /> <br /> 간단합니다. ^^ 내부 코드에서 await을 써야 한다면 async를 붙이면 되고, 없으면 떼는 것을 권장합니다. 하지만 이런 규칙을 굳이 염두에 두지 않아도 되는 것은, 애당초 Visual Studio 코드 창에서 편집하게 되면 await이 없는 async 메서드에 대해서는 CS1998 경고를 발생하기 때문입니다.<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.<br /> </div><br /> <br /> 그래도 저 규칙을 인지하고 있으면, 위와 같은 사례에서 볼 수 있듯이 굳이 DelayAsync와 같이 코딩하는 상황을 DelayTask와 같이 (능동적으로) 바꿔 쓸 수 있을 것입니다. 참고로, 아래의 글에서도,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Long Story Short: Async/Await Best Practices in .NET ; <a target='tab' href='https://medium.com/@deep_blue_day/long-story-short-async-await-best-practices-in-net-1f39d7d84050'>https://medium.com/@deep_blue_day/long-story-short-async-await-best-practices-in-net-1f39d7d84050</a> </pre> <br /> "Consider using return Task instead of return await"라는 소제목으로 이에 대한 설명을 하고 있습니다.<br /> <br /> 그렇긴 한데, <a target='tab' href='https://www.sysnet.pe.kr/2/0/13422#part2'>다른 글에서 설명</a>하겠지만 단순히 Task를 반환하는 것은 약간의 혼란이 발생할 수 있는 여지가 있어서 좀 아쉬운 점이 있습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> await 호출의 대상이 되는 메서드를 awaitable하다고 하며, 이 조건을 만족하기 위해서는 <a target='tab' href='https://www.sysnet.pe.kr/2/0/11456'>GetAwaiter 메서드를 가진 타입</a>이기만 하면 됩니다. 일례로 우리가 자주 접하게 되는 <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task'>Task</a> 타입이 그러한 대표적인 사례입니다. 이후 마이크로소프트가 GC를 괴롭히지 않기 위한 보조 타입으로 만든 <a target='tab' href='https://www.sysnet.pe.kr/2/0/13114'>ValueTask</a> 역시 GetAwaiter 메서드를 가지고 있습니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1220
(왼쪽의 숫자를 입력해야 합니다.)