C# Asenkron Streamler (IAsyncEnumerable)
C#’ta IAsyncEnumerable ile asenkron stream kullanımını öğrenin. Veri akışını adım adım işleme senaryoları örneklerle anlatılıyor.
Modern .NET uygulamalarında veriler çoğu zaman parça parça ve zaman içinde gelir: ağdan akan kayıtlar, log satırları, sensör verileri…
IAsyncEnumerable<T>, bu tür akışları asenkron ve ertelemeli (lazy) biçimde tüketmeyi sağlar.
Üretim tarafında async + yield return, tüketim tarafında ise await foreach kullanılır.
IAsyncEnumerable Nedir?
IAsyncEnumerable<T> bir veri dizisini asenkron olarak üretip tüketmenize yarar.
Senkron IEnumerable<T>’dan farkı, her adımda bekleme olabilmesidir (ör. I/O gecikmesi).
Tüketim için await foreach yazılır; her adımda bir sonraki öğe hazır olana kadar await edilir.
// Basit tüketim
await foreach (var item in GetNumbersAsync())
{
Console.WriteLine(item);
}
Async Iterator: async + yield return
Asenkron üretici (producer) metot yazarken dönüş türü IAsyncEnumerable<T> olur;
gövde içinde await ile bekleyip yield return ile öğe üretirsiniz.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
static async IAsyncEnumerable<int> GetNumbersAsync()
{
for (int i = 1; i <= 5; i++)
{
await Task.Delay(500); // I/O veya zaman alan iş simülasyonu
yield return i; // her 500 ms'de bir yeni sayı
}
}
// Tüketim
await foreach (var n in GetNumbersAsync())
{
Console.WriteLine($"Geldi: {n}");
}
await foreach ile Tüketim
await foreach, asenkron akışlardan öğe çekerken UI’yi veya iş parçacığını bloklamaz.
Her adımda veri hazır olana dek beklenir; veri gelince döngü kaldığı yerden devam eder.
await foreach (var kayit in OkuLogKayitlariAsync())
{
// Kayıt geldiği anda işle
IslemeAl(kayit);
}
Hata Yönetimi ve using
Asenkron akış tüketiminde klasik try/catch kullanabilirsiniz.
Akış içinde veya tüketimde hata fırlarsa await foreach sırasında yakalanır.
try
{
await foreach (var item in GetNumbersAsync())
Console.WriteLine(item);
}
catch (Exception ex)
{
Console.WriteLine("Hata: " + ex.Message);
}
Asenkron kaynaklar için IAsyncDisposable ve await using kullanabilirsiniz.
await using var kaynak = await KaynakAcAsync();
await foreach (var x in kaynak.Akim())
{
// ...
}
İptal Desteği (CancellationToken)
Uzun süren akışları durdurmak için tüketimi iptal edebilirsiniz.
Üretici tarafta [EnumeratorCancellation] ile token’ı yakalayıp düzenli aralıklarla kontrol edin.
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
static async IAsyncEnumerable<int> SayAsync(
int basla, int adet, [EnumeratorCancellation] CancellationToken ct = default)
{
for (int i = 0; i < adet; i++)
{
ct.ThrowIfCancellationRequested();
await Task.Delay(300, ct);
yield return basla + i;
}
}
// Tüketim
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(1)); // 1 sn sonra iptal
await foreach (var n in SayAsync(10, 100, cts.Token))
{
Console.WriteLine(n);
}
Karşılaştırma: IEnumerable<Task<T>> vs IAsyncEnumerable<T>
- IEnumerable<Task<T>>: Bütün görevleri üretip sonra tüketirsiniz; geri basınç (backpressure) zayıftır.
- IAsyncEnumerable<T>: Öğeler gerektikçe üretilir; bellek kullanımı ve zamansal dağılım daha verimlidir.
Dosyadan Satır Satır Asenkron Okuma (Sarmalayıcı Örnek)
Aşağıdaki örnek, bir dosyayı satır satır asenkron okuyan bir akış üretir.
Her satır geldiğinde tüketiciye yield return ile aktarılır.
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
static async IAsyncEnumerable<string> ReadLinesAsync(string path)
{
using var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read,
bufferSize: 4096, useAsync: true);
using var sr = new StreamReader(fs);
while (!sr.EndOfStream)
{
var line = await sr.ReadLineAsync(); // her satır geldiğinde
if (line is not null)
yield return line;
}
}
// Tüketim
await foreach (var line in ReadLinesAsync("log.txt"))
{
Console.WriteLine(line);
}
Akış Üzerinde Basit Filtreleme (Pipeline)
Asenkron akışlar birbirine bağlanarak pipeline oluşturulabilir: üret → filtrele → dönüştür → tüket.
static async IAsyncEnumerable<int> FiltreleAsync(IAsyncEnumerable<int> kaynak)
{
await foreach (var n in kaynak)
{
if (n % 2 == 0) // çift sayıları geçir
yield return n * 10; // dönüştür
}
}
// Kullanım
await foreach (var x in FiltreleAsync(GetNumbersAsync()))
{
Console.WriteLine(x); // 20, 40, ...
}
UI Senaryosu: WPF'te await foreach
WPF'te uzun süren akışlar UI’yi dondurmaz; her öğe geldiğinde UI güncellenebilir.
UI güncellemeleri gerekiyorsa Dispatcher ile UI thread’ine dönün.
// Örn: Sensör verisini akıştan okuyup UI'ya yaz
await foreach (var v in OkuSensorAsync())
{
Dispatcher.Invoke(() => txtDurum.Text = $"Son değer: {v}");
}
İpuçları ve Dikkat Edilecekler
- Akış üretiminde
yield returnile parça parça üretin; büyük listeleri tek seferde belleğe almayın. - İptal (
CancellationToken) desteği verin; uzun akışlar kullanıcı tarafından durdurulabilmeli. - Hataları
try/catchile doğru seviyede yönetin; tüketicide yakalayıp loglayın. - UI güncellemelerinde
Dispatcherkullanın; UI thread dışından kontrol erişimi yapmayın.
TL;DR
- IAsyncEnumerable<T> asenkron ve ertelemeli veri akışları içindir.
- Üretici:
async+yield return; Tüketici:await foreach. - İptal için
[EnumeratorCancellation]veCancellationTokenkullanın. - IEnumerable<Task<T>> yerine IAsyncEnumerable<T> bellek ve zamanlama açısından daha verimli olabilir.
- UI’da güncelleme gerekiyorsa
Dispatcherile UI thread’ine dönün.
İlişkili Makaleler
C# Asenkron Programlama Temelleri (async/await)
C#’ta async ve await kullanımını öğrenin. Asenkron işlemler, Task yapısı ve performanslı uygulama akışları örneklerle anlatılıyor.
C# Process ve Thread Yönetimi
C#’ta process ve thread yönetimini öğrenin. Çok iş parçacıklı yapı, süreç kontrolü ve sistem kaynaklarıyla çalışma örneklerle.
C# Task Parallel Library (TPL) ve Paralel Programlama
C#’ta Task Parallel Library ve paralel programlamayı öğrenin. Task, Parallel ve eşzamanlı işlem senaryoları örneklerle anlatılıyor.