C#'ta async/await: Task, deadlock ve sık yapılan hatalar
C#'ta async ve await, ağ istekleri, dosya ve veritabanı işlemleri gibi bekleme içeren işleri programı kilitlemeden yapmayı sağlar. Kullanımı kolay görünür ama birkaç yanlış alışkanlık zor bulunan hatalara yol açar.
Temel kullanım
public async Task<string> SayfayiGetirAsync(string url)
{
using var client = new HttpClient();
return await client.GetStringAsync(url);
}
await beklenen iş bitene kadar metodu askıya alır, ama iş parçacığını (thread) bloklamaz. Bu sırada uygulama başka işlere devam eder.
Hata 1: .Result ve .Wait() ile beklemek
var sonuc = SayfayiGetirAsync(url).Result; // tehlikeli
Masaüstü (WinForms, WPF) uygulamalarında ve eski ASP.NET'te bu satır deadlock'a yol açabilir: Ana iş parçacığı sonucu beklerken, görev de devam etmek için ana iş parçacığının boşalmasını bekler ve uygulama donar. Çözüm, zincirin en başına kadar async kullanmaktır ("async all the way").
Hata 2: async void
async void metotların hataları yakalanamaz ve uygulamayı çökertebilir. Yalnızca olay işleyicilerinde (button_Click gibi) kullanılmalıdır. Diğer her yerde async Task dön.
Hata 3: Her istekte yeni HttpClient
Yukarıdaki örnekte basitlik için using ile oluşturduk, ama yoğun kullanılan uygulamalarda her istekte yeni HttpClient açmak soket tükenmesine yol açabilir. ASP.NET Core'da IHttpClientFactory kullan; diğer uygulamalarda tek bir örneği paylaş.
Hata 4: İşleri gereksiz yere sırayla beklemek
Birbirinden bağımsız işleri tek tek beklemek yerine aynı anda başlat:
var t1 = KullanicilariGetirAsync();
var t2 = SiparisleriGetirAsync();
await Task.WhenAll(t1, t2);
ConfigureAwait(false)
Kütüphane kodu yazıyorsan await x.ConfigureAwait(false) kullanmak, devam kısmının özgün bağlama dönmeye çalışmasını engeller ve deadlock riskini azaltır. ASP.NET Core uygulama kodunda genellikle gerekmez.
İptal desteği
Uzun süren işlerde CancellationToken parametresi almak, kullanıcı sayfadan çıktığında ya da istek iptal edildiğinde gereksiz işi durdurur:
public async Task IsleAsync(CancellationToken ct)
{
await Task.Delay(1000, ct);
}
Kodunda donma ya da beklenmeyen bir istisna varsa ilgili metodu ve çağrıldığı yeri bu konuya ekleyerek sorabilirsin.