Share This Post

.NET / C# / Öne Çıkanlar

C# Asenkron Anti-Pattern’ler | Bölüm 1

Merhabalar;
C#’da async ve await, asenkron kod yazmayı basitleştirerek büyük bir iş yaptı, fakat ne yazık ki bu tarz güzel özellikler bile yanlış yapmaktan bizi koruyamıyor. 

Bu yazıda, kod incelemelerinde karşılaştığım en yaygın asenkron kodlama hatalarına veya anti-pattern’lere değinmek istiyorum.

1- await Kullanmayı Unutmayın;

Task ya da Task<T> döndüren bir method çağırdığınız da, dönüş değerini göz ardı etmemenizde fayda vardır. Daha sonraya erteleyecek şekilde beklemekten kaçınabilirsiniz ama, çoğu durumda bu fonksiyonu beklemeniz anlamına gelir.

Aşağıda ki örnekte, Task.Delay‘ı çağırıyoruz, ancak onu beklemiyoruz, “Sonra” mesajı hemen yazdırılacak çünkü Task.Delay (1000) bir saniyede tamamlayacak bir görev döndürüyor, fakat bu görevin tamamlanması için onun görevi tamamlamasını bekleyen hiç bir kod yazmıyoruz.

Console.WriteLine("Önce");
Task.Delay(1000);
Console.WriteLine("Sonra");

Eğer bu hatayı, Task döndüren bir async methodu ile yaparsak, derleyici hata verecektir.

Çünkü sonuç beklenilmediğinden, ilgili methodun yürütülmesi task sonuçlanmadan devam eder. Task sonunda ‘await‘ operatörünü kullanmayı deneyin.

Fakat, async olarak belirtilmemiş yöntemlerde veya görev döndürme yöntemlerinde, C# derleyicisi hata vermeden derlemeyi yapabilir. Buna dikkat etmek gerekir.

2- ignoredTask;

Bazen, bilerek eş zamanlı olmayan bir yöntemin sonucunu beklemek istemeyebiliriz. Bunun bazı sebepleri de olabilir, belki de diğer işleri sürdürürken arka planda çalışmasını istediğimiz, uzun süren bir operasyondur. Örneğin;

var ignoredTask = DoSomethingAsync();

Bu yaklaşımın tehlikesi, “DoSomethingAsync” tarafından verilen hataların yakalanamamasıdır. Aslında görevin tamamlanıp, tamamlanamadığını bilemediğimiz anlamına gelir, en kötü senaryoda süreç sonlanabilir.

Bu nedenle bunu kullanırken dikkatli olmalıyız, en azından arka planda görevin bir kuyruğa mesaj göndermesini sağlayabiliriz.

3- async Sürümü;

.NET’de bir method fazla zaman alıyor veya bir disk veya network de performans sorunu gerçekleşiyorsa, kullandığımız yöntemin genellikle bir async sürümü vardır.

Örneğin, Thread.Sleep yerine Task.Delay tercih edilebilir. dbContext.SaveChanges yerine dbContext.SaveChangesAsync ve fileStream.Read yerine fileStream.ReadAsync tercih edilebilir. 

Bu sayede, thread’leri, daha fazla sayıda istek işleyebilmesi ve başka işler yapabilmesi için serbest bırakır.

4- await Olmadan try catch Kullanımı;

Bildiğimiz kullanışlı bir optimizasyon var. Örneğin, async çağrısı yapan çok basit bir async yöntemimiz var:

public async Task SendUserLoggedInMessage(Guid userId)
{
    var userLoggedInMessage = new UserLoggedInMessage() { UserId = userId };
    await messageSender.SendAsync("mytopic", userLoggedInMessage);
}

Bu durumda, async ve await kullanmanıza gerek yok. Aşağıdakileri basitçe yapabilirdik.

public Task SendUserLoggedInMessage(Guid userId)
{
    var userLoggedInMessage = new UserLoggedInMessage() { UserId = userId };
    return messageSender.SendAsync("mytopic", userLoggedInMessage);
}

Arka planda, await kullanan kod, biraz daha verimli kod üretir.

Ancak, fonksiyonu aşağıdaki gibi güncellediğimizi varsayalım:

public Task SendUserLoggedInMessage(Guid userId)
{
    var userLoggedInMessage = new UserLoggedInMessage() { UserId = userId };
    try
    {
        return messageSender.SendAsync("mytopic", userLoggedInMessage);
    }
    catch (Exception ex)
    {
        logger.Error(ex, "Failed to send message");
        throw;
    }
}

Düzgün görünüyor, fakat aslında catch beklediğiniz etkiye sahip olmayacak. SendAsync‘ten gelen Task çalıştırılırken alınabilecek tüm hataları yakalayamaz. Çünkü biz sadece bu Task oluştururken verilen hataları yakaladık. Bu görev sırasında herhangi bir noktada hataları yakalamak istiyorsak, await kullanımına tekrar ihtiyacımız var.

public async Task SendUserLoggedInMessage(Guid userId)
{
    var userLoggedInMessage = new UserLoggedInMessage() { UserId = userId };
    try
    {
        await messageSender.SendAsync("mytopic", userLoggedInMessage);
    }
    catch (Exception ex)
    {
        logger.Error(ex, "Failed to send message");
        throw;
    }
}

Artık catch, SendAsync görevini yürütürken herhangi bir noktada alınan hataların tümünü yakalayabilecektir .

Share This Post

Yazılım felan filan...

Leave a Reply