40. IAsyncEnumerable<T> / await foreach 의 상태 머신·취소·ConfigureAwait
난이도 상 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// [주제] IAsyncEnumerable<T> / await foreach 의 상태 머신, 취소, ConfigureAwait
//
// 게임서버에서 대량 결과를 스트리밍(예: DB 커서, 존 엔티티 순회)할 때 쓰는
// 비동기 스트림이다. 아래 코드의 동작을 예측하고 내부 동작을 설명하라.
//
// [과제]
// 1) StreamScores 같은 메서드(yield return + await 혼합)를 컴파일러가
// 무엇으로 변환하는가? IEnumerator 상태 머신과 무엇이 다른가?
// (MoveNextAsync 가 ValueTask<bool> 를 반환하는 이유 포함)
// 2) await foreach 는 어떤 인터페이스/메서드를 호출하며, 열거 종료 시
// DisposeAsync 는 언제 불리는가? 소비자가 break 하면?
// 3) CancellationToken 을 [EnumeratorCancellation] 로 받는 것과
// WithCancellation(token) 으로 넘기는 것의 관계를 설명하라. token 이
// 생성 시점(StreamScores 호출)과 열거 시작 시점 중 어디에 묶이는가?
// 4) ConfigureAwait(false) 를 await foreach 에 적용하면 무엇이 달라지는가?
// 생산자 내부의 await 에는 어떻게 전파되는가?
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
public class ScoreStreamer
{
public async IAsyncEnumerable<int> StreamScores(
int total,
[EnumeratorCancellation] CancellationToken ct = default)
{
for (int i = 0; i < total; i++)
{
ct.ThrowIfCancellationRequested();
int page = await FetchPage(i, ct); // 비동기 I/O
yield return page; // 하나씩 산출
}
}
private Task<int> FetchPage(int i, CancellationToken ct) => Task.FromResult(i * 10);
public async Task<long> Consume(int total, CancellationToken external)
{
long sum = 0;
await foreach (var s in StreamScores(total)
.WithCancellation(external)
.ConfigureAwait(false))
{
sum += s;
if (sum > 1000) break;
}
return sum;
}
} 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.