Microsoft MVP성태의 닷넷 이야기
닷넷: 2147. C# - 비동기 메서드의 async 예약어 유무에 따른 차이 [링크 복사], [링크+제목 복사],
조회: 3686
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 1개 있습니다.)
(시리즈 글이 9개 있습니다.)
.NET Framework: 698. C# 컴파일러 대신 직접 구현하는 비동기(async/await) 코드
; https://www.sysnet.pe.kr/2/0/11351

.NET Framework: 716. async 메서드의 void 반환 타입 사용에 대하여
; https://www.sysnet.pe.kr/2/0/11414

.NET Framework: 717. Task를 포함하지 않는 async 메서드의 동작 방식
; https://www.sysnet.pe.kr/2/0/11415

.NET Framework: 719. Task를 포함하는 async 메서드의 동작 방식
; https://www.sysnet.pe.kr/2/0/11417

.NET Framework: 731. C# - await을 Task 타입이 아닌 사용자 정의 타입에 적용하는 방법
; https://www.sysnet.pe.kr/2/0/11456

.NET Framework: 737. C# - async를 Task 타입이 아닌 사용자 정의 타입에 적용하는 방법
; https://www.sysnet.pe.kr/2/0/11484

.NET Framework: 813. C# async 메서드에서 out/ref/in 유형의 인자를 사용하지 못하는 이유
; https://www.sysnet.pe.kr/2/0/11850

닷넷: 2138. C# - async 메서드 호출 원칙
; https://www.sysnet.pe.kr/2/0/13405

닷넷: 2147. C# - 비동기 메서드의 async 예약어 유무에 따른 차이
; https://www.sysnet.pe.kr/2/0/13421




C# - 비동기 메서드의 async 예약어 유무에 따른 차이

현실적으로 await 예약어를 사용해 호출할 수 있는 대상 메서드는 Task를 반환해야 합니다. 단지, 상황에 따라 async 예약어를 곁들일 수 있는데요, 아래의 예제 코드를 보면,

namespace ConsoleApp1;

internal class Program
{
    static async Task Main(string[] args)
    {
        DateTime now = DateTime.Now;
        await DelayTask();
        await DelayAsync();
        Console.WriteLine($"{(DateTime.Now - now).TotalSeconds}"); // 4초 소요
    }

    static Task DelayTask()
    {
        return Task.Delay(1000);
    }

    static async Task DelayAsync()
    {
        await Task.Delay(3000);
    }
}

결국 await 호출을 하는 측의 입장에서 보면 지정한 시간 동안 대기하는 동작은 똑같지만, DelayTask와 DelayAsync 메서드의 내부 코드에는 살짝(?) 다른 점이 존재합니다. 실제로 위의 예제 코드는 C# 컴파일러에 의해 각각 아래와 같이 처리됩니다.

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;
}

게다가 DelayAsync의 경우 위의 코드뿐만 아니라 내부 코드를 분할해 실행하는 상태 머신을 구현한 클래스를 별도로 하나 더 생성하기 때문에,

[CompilerGenerated]
private sealed class <DelayAsync>d__2 : IAsyncStateMachine
{
    // ...[생략]...

    void IAsyncStateMachine.MoveNext()
    {
        // ...[생략]...
    }

    [DebuggerHidden]
    void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
    {
    }
}

복잡도가 더 증가합니다. 단순히 생각해 봐도, 어차피 같은 역할을 하는 것이라면 async를 적용하지 않는 편이 더 깔끔하고 성능상으로도 (작게나마) 이득을 볼 수 있습니다. 그렇다면 async를 붙이는 적절한 기준이 있을까요?

간단합니다. ^^ 내부 코드에서 await을 써야 한다면 async를 붙이면 되고, 없으면 떼는 것을 권장합니다. 하지만 이런 규칙을 굳이 염두에 두지 않아도 되는 것은, 애당초 Visual Studio 코드 창에서 편집하게 되면 await이 없는 async 메서드에 대해서는 CS1998 경고를 발생하기 때문입니다.

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.


그래도 저 규칙을 인지하고 있으면, 위와 같은 사례에서 볼 수 있듯이 굳이 DelayAsync와 같이 코딩하는 상황을 DelayTask와 같이 (능동적으로) 바꿔 쓸 수 있을 것입니다. 참고로, 아래의 글에서도,

Long Story Short: Async/Await Best Practices in .NET
; https://medium.com/@deep_blue_day/long-story-short-async-await-best-practices-in-net-1f39d7d84050

"Consider using return Task instead of return await"라는 소제목으로 이에 대한 설명을 하고 있습니다.

그렇긴 한데, 다른 글에서 설명하겠지만 단순히 Task를 반환하는 것은 약간의 혼란이 발생할 수 있는 여지가 있어서 좀 아쉬운 점이 있습니다.




await 호출의 대상이 되는 메서드를 awaitable하다고 하며, 이 조건을 만족하기 위해서는 GetAwaiter 메서드를 가진 타입이기만 하면 됩니다. 일례로 우리가 자주 접하게 되는 Task 타입이 그러한 대표적인 사례입니다. 이후 마이크로소프트가 GC를 괴롭히지 않기 위한 보조 타입으로 만든 ValueTask 역시 GetAwaiter 메서드를 가지고 있습니다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 10/18/2023]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 31  32  33  34  35  36  37  38  39  40  41  42  43  44  [45]  ...
NoWriterDateCnt.TitleFile(s)
12524정성태1/31/20218649개발 환경 구성: 530. 기존 github 프로젝트를 Azure Devops의 빌드 Pipeline에 연결하는 방법 [1]
12523정성태1/31/20218685개발 환경 구성: 529. 기존 github 프로젝트를 Azure Devops의 Board에 연결하는 방법
12522정성태1/31/202110229개발 환경 구성: 528. 오라클 클라우드의 리눅스 VM - 9000 MTU Jumbo Frame 테스트
12521정성태1/31/202110159개발 환경 구성: 527. 이더넷(Ethernet) 환경의 TCP 통신에서 MSS(Maximum Segment Size) 확인 [1]
12520정성태1/30/20218698개발 환경 구성: 526. 오라클 클라우드의 VM에 ping ICMP 여는 방법
12519정성태1/30/20217688개발 환경 구성: 525. 오라클 클라우드의 VM을 외부에서 접근하기 위해 포트 여는 방법
12518정성태1/30/202125202Linux: 37. Ubuntu에 Wireshark 설치 [2]
12517정성태1/30/202112872Linux: 36. 윈도우 클라이언트에서 X2Go를 이용한 원격 리눅스의 GUI 접속 - 우분투 20.04
12516정성태1/29/20219478Windows: 188. Windows - TCP default template 설정 방법
12515정성태1/28/202110714웹: 41. Microsoft Edge - localhost에 대해 http 접근 시 무조건 https로 바뀌는 문제 [3]
12514정성태1/28/202111017.NET Framework: 1021. C# - 일렉트론 닷넷(Electron.NET) 소개 [1]파일 다운로드1
12513정성태1/28/20219049오류 유형: 698. electronize - User Profile 디렉터리에 공백 문자가 있는 경우 빌드가 실패하는 문제 [1]
12512정성태1/28/20218812오류 유형: 697. The program can't start because VCRUNTIME140.dll is missing from your computer. Try reinstalling the program to fix this problem.
12511정성태1/27/20218581Windows: 187. Windows - 도스 시절의 8.3 경로를 알아내는 방법
12510정성태1/27/20219012.NET Framework: 1020. .NET Core Kestrel 호스팅 - Razor 지원 추가 [1]파일 다운로드1
12509정성태1/27/20219899개발 환경 구성: 524. Jupyter Notebook에서 C#(F#, PowerShell) 언어 사용을 위한 환경 구성 [3]
12508정성태1/27/20218483개발 환경 구성: 523. Jupyter Notebook - Slide 플레이 버튼이 없는 경우
12507정성태1/26/20218588VS.NET IDE: 157. Visual Studio - Syntax Visualizer 메뉴가 없는 경우
12506정성태1/25/202111959.NET Framework: 1019. Microsoft.Tye 기본 사용법 소개 [1]
12505정성태1/23/20219586.NET Framework: 1018. .NET Core Kestrel 호스팅 - Web API 추가 [1]파일 다운로드1
12504정성태1/23/202110730.NET Framework: 1017. .NET 5에서의 네트워크 라이브러리 개선 (2) - HTTP/2, HTTP/3 관련 [1]
12503정성태1/21/20219049오류 유형: 696. C# - HttpClient: Requesting HTTP version 2.0 with version policy RequestVersionExact while HTTP/2 is not enabled.
12502정성태1/21/20219830.NET Framework: 1016. .NET Core HttpClient의 HTTP/2 지원파일 다운로드1
12501정성태1/21/20218866.NET Framework: 1015. .NET 5부터 HTTP/1.1, 2.0 선택을 위한 HttpVersionPolicy 동작 방식파일 다운로드1
12500정성태1/21/20219414.NET Framework: 1014. ASP.NET Core(Kestrel)의 HTTP/2 지원 여부파일 다운로드1
12499정성태1/20/202110659.NET Framework: 1013. .NET Core Kestrel 호스팅 - 포트 변경, non-localhost 접속 지원 및 https 등의 설정 변경 [1]파일 다운로드1
... 31  32  33  34  35  36  37  38  39  40  41  42  43  44  [45]  ...