Yükleniyor...

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>


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


TL;DR

  • IAsyncEnumerable<T> asenkron ve ertelemeli veri akışları içindir.
  • Üretici: async + yield return; Tüketici: await foreach.
  • İptal için [EnumeratorCancellation] ve CancellationToken kullanın.
  • IEnumerable<Task<T>> yerine IAsyncEnumerable<T> bellek ve zamanlama açısından daha verimli olabilir.
  • UI’da güncelleme gerekiyorsa Dispatcher ile UI thread’ine dönün.

İlişkili Makaleler

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.