C# 8.0에서 새롭게 도입된 비동기 스트림(Async Streams)은 데이터를 스트리밍 형태로 다루는 소스를 모델링하는 기능입니다. 실제 애플리케이션에서 데이터 스트림은 네트워크 요청, 파일 읽기, 센서 데이터 수신 등 다양한 상황에서 요소를 비동기적으로 검색하거나 생성하는 경우가 많습니다.
C# 8.0 이전에는 시퀀스를 생성하는 메서드에서 yield return을 사용하려면 반드시 동기 방식이어야 했습니다. 하지만 이제는 async 한정자로 선언된 메서드 안에서도 yield return을 사용해 요소를 하나씩 반환할 수 있게 되었습니다.
생성된 비동기 스트림은 await foreach 루프를 통해 손쉽게 소비(consume)할 수 있습니다.
기존 문법 vs 비동기 스트림 문법
아래와 같은 기존의 동기 방식 코드는,
static IEnumerable<string> Message(){
yield return "Hello!";
yield return "Hello!";
}IAsyncEnumerable<T> 인터페이스를 사용하는 비동기 버전으로 대체할 수 있습니다.
static async IAsyncEnumerable<string> MessageAsync(){
await Task.Delay(2000);
yield return "Hello!";
await Task.Delay(2000);
yield return "Hello!";
}전체 예제 코드
다음은 비동기 스트림을 생성하고 await foreach로 소비하는 완전한 프로그램 예제입니다.
class Program{
public static async Task Main(){
await foreach (var item in MessageAsync()){
System.Console.WriteLine(item);
}
Console.ReadLine();
}
static async IAsyncEnumerable<string> MessageAsync(){
await Task.Delay(2000);
yield return "Hello!";
await Task.Delay(2000);
yield return "Hello!";
}
}실행 결과
각 요소가 반환되기 전에 2초씩 지연되며, 콘솔에는 아래와 같이 출력됩니다.
Hello! Hello!
정리
비동기 스트림은 데이터가 도착하는 즉시 처리할 수 있게 해주므로, 대용량 데이터 조회나 실시간 데이터 피드 처리에 특히 유용합니다. 핵심 포인트를 정리하면 다음과 같습니다.
- IAsyncEnumerable<T>: 비동기적으로 열거 가능한 시퀀스를 나타내는 인터페이스
- async + yield return: 비동기 메서드 내에서 요소를 순차적으로 생성
- await foreach: 각 요소를 비동기적으로 대기하며 소비