Web ortamında async/await nasıl çalışır

İlker Erhalim
3 min readJan 13, 2021

Async ve await keylerinin temel amacı anlaşılması kolay ve geliştiriciyi Task sınıfının içeriğinden soyutlayarak daha rahat uygulama geliştirmesini sağlamaktır. Çalışma mantığı Task Koordinasyonu yazımda anlattığım ContinueWith metoduna benzer. Amacı main thread i bekletmeden işlem yapabilmektir.

Kullanımı oldukça basittir; işlemin yapılacağı metod async keyi ile tanımlanır ve içerisinde awaitable olan (Geriye bir Task ya da Task<T> döndüren) bir başka metot await key i ile çağırılır.

Masaüstü uygulama geliştirirken nasıl çalıştığını anlamak oldukça kolaydır, bu yüzden bu yazımda örnekleri .net core ile oluşturduğum bir Restful API üzerinden anlatmaya çalışacağım.

async/await syntax i ile ilgili çok fazla kaynak mevcut, bu yüzden syntaxten uzun uzun bahsetmek istemiyorum.

[HttpGet(“/api/items”)]
public async Task<IActionResult> GetItems()
{
var items = await LoadItemsAsnyc();
return Ok(items);
}

Async/await ile ilgili yazıların hemen hepsinde buna benzer bir örnek verilir ve bu işlemin NonBlocking olduğunu söylenir.

Daha önce web ortamında geliştirme yaptıysanız aklınızda hemen bir soru belirir.

Await keyword ü işlemin bitmesini bekliyor, uygulama bir response dönene kadar bekleyecekse bütün bu uğraş niye?

Bu soruya cevap vermek için öncelikle await keywordü ile Task sınıfındaki Wait metodunun farkını anlamak gerekiyor.

Task sınıfındaki Wait metodu mevcut thread’i task tamamlanana kadar kilitler. Await keywordü ise thread i kilitlemez, bekleme işlemini farklı bir thread içerisinde yapar ve işlem tamamlandığında await kullanılan satırın altındaki işlemler farklı bir thread içerisinde devam edebilir.

Yukarıdaki örneği çalıştırdığınızda metot içerisinde birden fazla thread kullanıldığını göreceksiniz. (İlk çalışan thread tekrar boşa çıktıysa işlemin aynı thread üzerinde çalışma ihtimali var)

Output aşağıdakine benzer olacaktır;

Request handled at:4
LoadItemsAsnyc method running at:4
After await, current thread id:5

UIThreadin bulunduğu uygulamalarda, (Windows Forms vb.) bekleme işlemi farklı thread içerisinde yapılır, ancak bekleme tamamlandıktan sonra işlemlerin kalanı yine UIThread içerisinde yapılır.

İhtiyacım olan tüm veriyi almadan response dönemiyorum, neden async/await kullanayım?

Arayüzü olan uygulamalarda async/await kullanmanın avantajı net bir şekilde gözüküyor, arayüz kilitlenmeden arka planda istediğimiz işlemleri yapabiliyoruz.

Ancak web ortamında bekleme işlemlerini farklı bir thread içerisinde yapmanın “Response Time” ı kısaltması mümkün değil. Hatta farklı threadler oluşturmanın maliyetinden dolayı response oluşturma süresi uzayacaktır.

Peki Faydası Nedir?

Bir .net uygulamasında main thread içerisinde gelen istekler sürekli dinlenir ve aynı thread içerisinde handle edilir. Yani bir request geldiğinde response dönene kadar gelen diğer requestler beklemeye alınır.

Bir .net web uygulaması aynı anda iki farklı isteğe cevap veremiyor mu?

Bu sorunun cevabı hem evet hem hayır, geliştirdiğiniz uygulamada işlemlerinizi ana thread üzerinde yaparsanız, uygulamanın yeni gelen isteklere cevap vermesi söz konusu değildir. Ancak uygulamayı host eden bir Web Server, uygulamayı birden fazla thread içerisinde çalıştırabilir ve bir işlem yapılırken diğer requestlerinde işlenmesine olanak tanıyabilir.

IIS içerisinde bir uygulamanın kaç process ile çalışabileceğini ayarlamak oldukça basittir.

Application Pools > Ayarlanmak istenilen Pool (Sağ tık) >Advanced Settings > Maximum Worker Processes

Maximum Worker Processes alanındaki değer uygulamanın aynı anda kaç farklı isteğe cevap verebilieceğini belirler.

Peki Diğer Requestlere Ne Oluyor?

Eğer Maximum Worker Processe ulaşıldıysa ve istek gelmeye devam ediyorsa diğer istekler bir queueya atılır ve sıra ile execute edilir. (Tanıdık geldi mi ?) Queue içerisinde olan istekler diğerleri tamamlanana kadar beklemeye devam eder. Maximum Queue limitine ulaşıldığında yeni gelen isteklerin tamamına 503 cevabı verilir.

IIS üzerinde Queue Length ve Maximum Worker Procecess ayarlarının gösterildiği resim. Queue L.= 1000 Max Work Proc=1

Yukarıdaki gibi ayarlanmış bir app pool içerisinde çalışan bir uygulama bir istek tamamlanmadan bin birinci istek geldiğinde 503 hatası alacaktır.

Ancak bu istekler işlenirken gerekli yerlerde async/await kullanılırsa await keyinden sonra ana thread serbest bırakılacak ve o sırada uygulama yeni bir worker processe ihtiyaç duymadan diğer istekleri işlemeye devam edebilecektir.

Microsoftun Performans Önerilerinde async/await ile ilgili olan bilgiler.

  • Task.Wait ve ya Task.Result kullanarak asenkron işlemi bloklamayın.
  • Ortak kullanılan alanlarda lock kullanmayın
  • await Task.Run(…) gibi bir işlem yapmayın.
  • Sık kullanılan kodları asenkron yapın.
  • Veriye erişirken, I/O işlemlerinde ve uzun süren işlemlerde eğer varsa Async metodu kullanın. (.net framework te async olan tüm metodlar Async ile bitmektedir.) Senkron olan bir işi asenkron hale getirmek için Task.Run kullanmayın.
  • Controller/Razor Page actionlarını asenkron yapın.
  • HttpRequestlerinin body kısmını asenkron olarak okuyun/yazın.
  • Request.Form yernine ReadFormAsync kullanmayı tercih edin.
  • HttpContext e birden fazla thread içerisinde erişmeyin. (Performans sıkıntısından çok hatalara sebep olacaktır.)

Detaylara ve Gerekçelere https://docs.microsoft.com/en-us/aspnet/core/performance/performance-best-practices?view=aspnetcore-5.0 adresinden ulaşabilirsiniz.

--

--