19. Task 조합의 예외·취소 의미론(WhenAll/await rethrow/CancellationToken/미관측 예외)
난이도 상 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// 주제: Task 조합의 예외·취소 의미론 — Task.WhenAll 의 예외 집계, await 의 rethrow,
// CancellationToken 협조적 취소, (그리고 WhenAny/async void 의 미관측 예외 함정).
//
// 여러 Task 를 WhenAll 로 묶어 await 하면 "예외가 어떻게 합쳐지고 무엇이 다시 던져지는가",
// 취소된 Task 가 Faulted 가 아니라 Canceled 상태가 되는 이유를 다룬다.
//
// 과제:
// 1) 이 프로그램의 정확한 표준출력을 예측하라(세 줄).
// 2) 두 Task 가 모두 예외를 던졌는데 catch 한 예외 타입/메시지가 왜 그것 하나인지
// (await 의 rethrow 규칙) 설명하고, all.Exception 에는 무엇이 몇 개 담기는지 밝혀라.
// 3) work 가 왜 Faulted 가 아니라 Canceled(IsCanceled==true) 로 끝나는지
// (토큰이 일치하는 OperationCanceledException 의 특별 취급) 설명하라.
// 4) (서술) Task.WhenAny 에서 "진 쪽" Task 의 예외가 미관측(unobserved)될 위험과,
// async void 의 예외가 왜 호출자에서 잡히지 않는지 설명하라.
using System;
using System.Threading;
using System.Threading.Tasks;
public static class Program
{
static async Task Faulting(string tag, int ms)
{
await Task.Delay(ms);
throw new InvalidOperationException(tag);
}
public static async Task Main()
{
// (2) WhenAll: 둘 다 던지지만 await 는 "첫 예외" 하나만 rethrow
var t1 = Faulting("A", 10);
var t2 = Faulting("B", 30);
var all = Task.WhenAll(t1, t2);
try { await all; }
catch (Exception ex) { Console.WriteLine($"caught {ex.GetType().Name}: {ex.Message}"); }
Console.WriteLine($"all.IsFaulted={all.IsFaulted} inner={all.Exception?.InnerExceptions.Count}");
// (3) 협조적 취소: 토큰이 일치하면 Canceled 상태
using var cts = new CancellationTokenSource();
var work = Task.Run(async () =>
{
for (int i = 0; i < 1000; i++)
{
cts.Token.ThrowIfCancellationRequested();
await Task.Delay(5);
}
}, cts.Token);
cts.Cancel();
try { await work; }
catch (OperationCanceledException) { }
Console.WriteLine($"work.IsCanceled={work.IsCanceled}");
}
} 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.