Task.WhenAll vs Parallel.ForEach

.NET Core’da Task.WhenAll ve Parallel.ForEach farkı, özellikle performans ve ölçeklenebilirlik açısından çok kritik.

.NET Core’da Task.WhenAll vs Parallel.ForEach – Farkları ve Ne Zaman Hangisi Kullanılmalı?

.NET dünyasında paralel programlama denildiğinde en sık karşılaşılan iki yapı:

  • Task.WhenAll
  • Parallel.ForEach

İkisi de aynı amaca hizmet ediyor gibi görünse de, aslında farklı senaryolar için tasarlanmışlardır. Yanlış seçim performans kaybına ve thread starvation (iş parçacığı tükenmesi) sorunlarına yol açabilir.

Bu yazıda teknik farklarını, performans etkilerini ve doğru kullanım senaryolarını inceleyeceğiz.

Task.WhenAll Nedir?

Task.WhenAll, birden fazla asenkron işlemi başlatır ve hepsinin tamamlanmasını bekler.

Genellikle I/O-bound (veritabanı, HTTP, dosya sistemi) işlemler için kullanılır.

public async Task GetDataAsync()
{
var task1 = GetFromApiAsync();
var task2 = GetFromDatabaseAsync();
var task3 = GetFromFileAsync();

await Task.WhenAll(task1, task2, task3);

}

Nasıl Çalışır?

Task’ler başlatılır.

Thread bloklanmaz.

İşlem I/O beklerken thread havuza geri bırakılır.

Tüm task’ler tamamlandığında devam eder.

Avantajları

✅ Thread block etmez
✅ Yüksek ölçeklenebilirlik
✅ Web API ve mikroservis mimarileri için ideal
✅ Async/await ile doğal uyumlu

Parallel.ForEach Nedir?

Parallel.ForEach, koleksiyon üzerindeki işlemleri çoklu thread kullanarak paralel çalıştırır.

Genellikle CPU-bound (yoğun hesaplama) işlemler için uygundur.

Parallel.ForEach(numbers, number =>
{
ProcessNumber(number);
});

Nasıl Çalışır?

  • ThreadPool’dan birden fazla thread alır.
  • İşlemleri paralel olarak dağıtır.
  • Her işlem ayrı thread’de çalışır.
  • İşlem bitene kadar thread meşgul kalır.

Avantajları

✅ CPU’yu maksimum kullanır
✅ Hesaplama yoğun işlemlerde hızlıdır
✅ Basit paralel işleme senaryolarında etkilidir

Temel Farklar

ÖzellikTask.WhenAllParallel.ForEach
Kullanım AmacıI/O-bound işlemlerCPU-bound işlemler
Thread KullanımıThread bloklanmazThread aktif kullanılır
Async DesteğiDoğal async/awaitAsync için uygun değil
Web API için✅ Çok uygun❌ Riskli
CPU Hesaplama❌ Zayıf✅ Çok iyi

En Büyük Hata: Parallel.ForEach ile Async Kullanmak

Şu kullanım yanlıştır:

Parallel.ForEach(items, async item =>
{
await CallApiAsync(item);
});

Bu yapı:

  • Async akışı bozabilir
  • Thread starvation’a sebep olabilir
  • Beklenmeyen hatalar üretebilir

Bunun yerine:

await Task.WhenAll(items.Select(item => CallApiAsync(item)));

kullanılmalıdır.

Ne Zaman Hangisini Kullanmalıyım?

✔ Eğer işlem I/O ise:

  • API çağrısı
  • Veritabanı sorgusu
  • Dosya okuma/yazma

Task.WhenAll kullan

Eğer işlem CPU-bound ise:

  • Görüntü işleme
  • Matematiksel hesaplama
  • Büyük veri transformasyonu

Parallel.ForEach kullan

Performans Açısından Özet

Modern .NET uygulamalarında async/await mimarisi önceliklidir.

Web uygulamalarında genellikle Task.WhenAll tercih edilmelidir.

Parallel.ForEach yanlış kullanılırsa sunucunun thread pool’unu tüketebilir.

Sonuç

Her iki yapı da paralellik sağlar ancak farklı problemleri çözer:

  • Task.WhenAll = Asenkron I/O paralelliği
  • Parallel.ForEach = CPU paralelliği

Yanlış araç seçimi sistem performansını düşürebilir. Özellikle ASP.NET Core projelerinde varsayılan tercihiniz çoğu zaman Task.WhenAll olmalıdır.