Giriş
Asenkron programlama, .NET ekosisteminin hem modernliği hem de verimliliğinin temelidir. Ancak, “sadece async/await” ile sınırlı değil! Gerçek bir projede, işte karşımıza çıkan tüm kavramlar:
- async / await
- .Result
- .Wait()
- .GetAwaiter().GetResult()
- ConfigureAwait(false)
- async void
- Task.Run()
- Task.FromResult()
- ValueTask
Her biri farklı amaca hizmet ediyor, farklı avantaj/dezavantaj getiriyor ve yanlış kullanıldığında uygulamanı “yavaş, kilitli veya çözülemez” hale getirebiliyor.

Bu yazıda, her birini en ince detayına kadar ne işe yarar, nasıl kullanılır, hangi durumda kesinlikle kaçınılmalı, kod örnekleri, “pro-level” ipuçları, sık yapılan hatalar ve performans senaryoları ile açıklayacağım.
1. async / await: Temel Prensipler ve Derin Pratikler
1.1 async Nedir?
asyncanahtar kelimesi, bir metodun “asenkron” olacağını bildirir.- Bu metodun içinde “await” kullanılabilir.
- Geriye çoğunlukla
Task,Task<T>, ya da modern .NET’teValueTask<T>döner. - Senkron metotlara async ekleyemezsin.
Kural:
Bir async metodun dönüş tipi
- void (SADECE event handler’da!)
- Task
- Task<T>
- ValueTask/ValueTask<T> olabilir.
Yanlış:
public async int Getir() // HATA! Dönüş tipi int olamaz.
Doğru:
public async Task<int> GetirAsync() { ... }
1.2 await Nedir?
- “await”, bir Task’in bitmesini “thread bloklamadan” bekler.
- await kullanırken metodun async olması gerekir.
- await, arka planda işlem tamamlanana kadar mevcut thread’i başka işlerde kullanır (özellikle IO-bound işlemler için harika).
Basit Örnek:
public async Task<int> GetirAsync()
{
await Task.Delay(1000);
return 42;
}
- Thread bloklanmaz: Kod, beklerken başka işlemler devam eder.
- await olmayan bir async metot uyarı verir, ama derlenir: Sadece uyarı (“This async method lacks ‘await’ operators…”)
1.3 async/await’in Faydaları
- UI Donmaz: WinForms/WPF/Mobile’da kullanıcı beklerken form donmaz.
- Ölçeklenebilirlik: Sunucu uygulamalarında thread bloklanmadan aynı anda binlerce iş yapılabilir.
- Kolay hata yönetimi: try/catch ile asenkron işlemler kolayca yönetilir.
2. .Result / .Wait(): Senkron Bekleme ve Tehlikeleri
2.1 .Result Nedir?
- Bir Task’in sonucunu SENKRON olarak döndürür.
- Thread bloklanır, yani uygulama durur ve “bitene kadar” hiçbir şey yapamaz.
- UI veya ASP.NET context’te deadlock riski taşır.
Örnek:
Task<int> t = GetirAsync();
int val = t.Result;
2.2 .Wait() Nedir?
- Task’in tamamlanmasını bekler, ama hiçbir değer döndürmez.
- Senkron blokajı oluşturur.
Örnek:
Task t = BekleAsync();
t.Wait();
2.3 Tehlikeler ve Hatalı Kullanım Senaryoları
- UI thread’de, .Result veya .Wait() kullanırsan uygulama kilitlenebilir!
- ASP.NET senaryolarında, context çakışması ile deadlock yaşanabilir.
Deadlock Örneği (Gerçek Dünya):
public async Task<string> VeriGetirAsync()
{
await Task.Delay(1000); // Simülasyon
return "tamam";
}
public string SenkronVeriGetir()
{
return VeriGetirAsync().Result; // DEADLOCK riski!
}
Açıklama: Eğer metot bir context’i (örn. UI thread’i) capture ettiyse, .Result beklerken asla tamamlanamaz ve kilitlenir.
3. .GetAwaiter().GetResult(): Farklı Exception Handling ve İnce Farklar
- .Result ile benzer şekilde senkron bekler.
- FARKI: Exception handling. .Result ile AggregateException gelir; GetAwaiter().GetResult() ile orijinal exception doğrudan fırlatılır.
Örnek:
try
{
var res = GetirAsync().GetAwaiter().GetResult();
}
catch (Exception ex)
{
// Doğrudan exception alınır.
}
Ne zaman tercih edilir?
- Genellikle test veya migration amaçlı, ya da özel durumlarda “asıl exception” yakalamak için.
- Yine de: Deadlock riski aynıdır!
4. ConfigureAwait(false): Context Yönetimi ve Performans
4.1 Temel Kullanım
- await edilen task’in devamında, orijinal “synchronization context”e (örn. UI veya ASP.NET request context) DÖNÜLMEMESİNİ sağlar.
- Performans ve context yönetimi için kritik!
Örnek:
public async Task VeriGetirAsync()
{
await Task.Delay(500).ConfigureAwait(false);
// Buradan sonra artık UI thread’inde değilsin!
}
4.2 Ne Zaman Kullanmalı?
- Kütüphane, servis veya arka plan işlerinde (UI ile ilgisi olmayan kodda) her zaman önerilir.
- UI uygulamalarında, await sonrası tekrar UI elemanlarına dokunacaksan kullanmamalısın.
4.3 Sık Yapılan Hatalar
-
UI uygulamasında: ConfigureAwait(false) sonrası, UI kontrollerine erişmeye kalkarsan, exception fırlatılır.
Yanlış:
await Task.Delay(500).ConfigureAwait(false);
label1.Text = "Bitti!"; // HATA: UI thread’inde değil!
5. async void: Neden Yalnızca Event Handler’da Kullanılmalı?
5.1 async void Nedir?
- Bir metodu async olarak işaretler ama geriye Task dönmez.
- Hatalar yukarıya “bubbling” yapmaz, yakalanamaz ve test edilemez.
Yanlış Kullanım:
public async void GenelKullanim()
{
await Task.Delay(1000);
throw new Exception("Hata!"); // try/catch ile yakalayamazsın!
}
Doğru Kullanım:
private async void Button_Click(object sender, EventArgs e)
{
await Task.Delay(500);
}
- Yani SADECE event handler’larda, zorunlu olduğunda async void kullanılır.
5.2 Neden Kaçınmalı?
- Exception propagation (hata fırlatma) ve await edilebilme kaybolur.
- Unit test yazılamaz, hata yönetimi zorlaşır.
6. Task.Run(): Arka Plan Thread ve CPU/IO Farkı
6.1 Task.Run() Nedir?
- Bir işi arka planda, thread pool’da çalıştırır.
- Aslında CPU-bound işler için idealdir; IO-bound işlemlerde gereksizdir.
Örnek:
public Task DosyaOkuVeİşleAsync(string yol)
{
return Task.Run(() =>
{
var lines = File.ReadAllLines(yol);
// Ağır işlem
});
}
Doğru Kullanım: Uzun süren veya “CPU’yu yoran” işleri (ör: büyük resim işleme, şifreleme) asenkron yapmak.
Yanlış Kullanım: Zaten asenkron olan bir IO işlemini Task.Run ile sarmak: Yalnızca thread tüketir, hiçbir avantajı yoktur.
7. Task.FromResult(): Hazır Sonucu Asenkron Olarak Döndürmek
- Bir değeri veya sonucu, asenkron metot imzası gereği Task olarak döndürmek için kullanılır.
- Özellikle testlerde, “cache’de varsa beklemeden ver” gibi durumlarda idealdir.
Örnek:
public Task<string> GetirAsync()
{
if (cache != null)
return Task.FromResult(cache);
return GetirDbAsync();
}
8. ValueTask ve ValueTask<T>: Hafif Asenkron Sonuçlar
8.1 ValueTask Nedir?
- .NET Core 2.0+ ile gelen, küçük/çoğunlukla senkron dönen işlerde heap allocation’ı azaltır.
- Eğer “sonuç çok hızlı ve genelde cache’den” geliyorsa, gereksiz Task objesi yaratmaz.
Örnek:
public ValueTask<int> GetirAsync()
{
if (cache.HasValue)
return new ValueTask<int>(cache.Value); // heap allocation yok!
return new ValueTask<int>(GetirDbAsync());
}
8.2 Dikkat Edilmesi Gerekenler
- ValueTask<T>, aynı anda birden fazla kez await edilemez (Task<T> ile uyumlu değildir).
- Karmaşık senaryolarda Task<T> kullanmak daha güvenlidir.
9. Sıkça Sorulan Sorular (SSS) ve Pro-level İpuçları
async/await ile sync metodu çağırmak doğru mu?
- Hayır. Sync metodu asenkron gibi göstermek sadece karmaşa yaratır. Gerçek asenkron IO/işlem yoksa, async/await kullanmak gereksizdir.
Her zaman ConfigureAwait(false) kullanmalı mıyım?
- Hayır!
- Kütüphane/servis yazıyorsan: Evet, performans için önerilir.
- UI/web uygulaması yazıyorsan, await sonrası UI ile işin varsa kullanma.
async void unit test yazılır mı?
- Yazılamaz. Sadece event’te zorunlu olduğunda kullan.
Task.Run ile IO işi arka plana atılır mı?
- Hayır. Gereksiz thread kullanımı, ölçeklenebilirlik kaybı.
ValueTask ile performans kazanılır mı?
- Çok nadir, sadece hot-path’de ve yüksek trafikli, genelde cache’den dönen işlerde.
.Result ve .Wait() ile Deadlock oluşur mu?
-
Evet! UI ve ASP.NET context’te ciddi kilitlenme riski.
Task.FromResult ve ValueTask ne zaman tercih edilir?
-
Elinde “hemen hazır” bir cevap varsa (cache gibi) ve metot async imzası gerektiriyorsa.
10. En Çok Yapılan Hatalar ve Gerçek Proje Tuzakları
- .Result veya .Wait()’i production UI/web thread’de kullanmak
- async void ile exception’ların yakalanmaması
- Gereksiz yere Task.Run ile async işlemi sarmak (özellikle IO-bound)
- ConfigureAwait(false) sonrası context kaybı ile UI’ya erişmeye çalışmak
- ValueTask’ı yanlış await etmek (birden fazla await edilemez!)
- Tüm async işlemleri aynı anda başlatmak, thread pool’u çökertmek
11. Kapsamlı Kod Örnekleriyle Karşılaştırma
1. Doğru async/await Kullanımı:
public async Task<string> VeriGetirAsync()
{
await Task.Delay(1000);
return "veri";
}
public async Task TestAsync()
{
string v = await VeriGetirAsync();
Console.WriteLine(v);
}
2. Deadlock’a Yol Açan Kötü Kullanım:
public string Senkron()
{
return VeriGetirAsync().Result; // UI/Web thread’inde deadlock!
}
3. ValueTask ile Hafif Sonuç:
public ValueTask<int> GetirAsync()
{
if (cache.HasValue)
return new ValueTask<int>(cache.Value);
return new ValueTask<int>(GetirDbAsync());
}
4. Task.Run ile CPU-bound ve IO-bound Karşılaştırması:
// DOĞRU: CPU-bound iş
await Task.Run(() => BüyükHesaplama());
// YANLIŞ: IO-bound iş
await Task.Run(() => HttpClient.GetStringAsync(url)); // Gereksiz
12. Özet ve En İyi Pratikler Tablosu
| Yöntem | Ne Zaman? | Avantaj/Dezavantaj |
|---|---|---|
| async/await | IO-bound, responsive UI, ölçeklenebilirlik | Doğru thread kullanımı, kolay hata yönetimi |
| .Result/.Wait() | SENKRON çağrı gerekirse (test/CLI) | Deadlock riski, blokaj, kaçınmak gerek |
| .GetAwaiter().GetResult() | Exception handling farkı için (özel durum) | Deadlock yine var, çok nadir gerekli |
| ConfigureAwait(false) | Kütüphane/serviste, context gerekmediğinde | Performans, context kaybı riski |
| async void | YALNIZCA event handler’da | Hata yakalanmaz, test yazılamaz |
| Task.Run() | CPU-bound işler | Thread pool yönetimi |
| Task.FromResult() | Hızlı cache/değer dönmek için | Allocation yok, çok pratik |
| ValueTask | Çok sık cache’den dönen, hızlı işler | Allocation yok, karmaşıklık riski |
13. Kaynaklar
- Microsoft Docs – Asynchronous Programming
- Stephen Cleary – Async/Await FAQ
- ConfigureAwait Explained
- Asynchronous Programming