37. async void vs async Task: 예외 전파와 완료 추적

난이도 상 해설 보기 →

결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커 (A)(B) 는 주목 위치 힌트다.

결함 코드 · C#
// 주제: async void vs async Task — 예외 전파·완료 추적·SynchronizationContext
//
// async 메서드의 반환 타입은 예외가 어디로 흘러가고 호출자가 완료를 어떻게 관측하는지를
// 결정한다. async Task 는 예외를 Task 에 저장했다가 await 시 다시 던지지만, async void 는
// 돌려줄 Task 가 없어 예외가 캡처된 SynchronizationContext(없으면 스레드풀)로 올라간다.
//
// 과제:
//  1) 아래 Main 실행 시 "caught void" 와 "caught task" 중 어느 catch 가 실제로 실행되는지
//     예측하라.
//  2) FireAndForget()(async void)에서 던진 예외가 왜 호출부의 try/catch 로 잡히지 않는지,
//     그 예외가 최종적으로 어디로 가는지(콘솔 앱/스레드풀, UI SynchronizationContext) 설명하라.
//  3) 이벤트 핸들러를 제외하면 async void 를 피해야 하는 이유와, 부득이할 때의 안전장치를 논하라.

using System;
using System.Threading.Tasks;

class Program
{
    static async void FireAndForget()
    {
        await Task.Yield();
        throw new InvalidOperationException("boom from async void");
    }

    static async Task FireTask()
    {
        await Task.Yield();
        throw new InvalidOperationException("boom from async Task");
    }

    static async Task Main()
    {
        try { FireAndForget(); }                    // (A) async void 예외를 잡으려는 시도
        catch (Exception ex) { Console.WriteLine("caught void: " + ex.Message); }

        try { await FireTask(); }                    // (B) async Task 예외를 await 로 잡기
        catch (Exception ex) { Console.WriteLine("caught task: " + ex.Message); }

        await Task.Delay(50);
        Console.WriteLine("Main done");
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

작성 후 위 해설 보기에서 모범 해설과 대조하세요.