Derinlemesine yazılım eğitimleri için kanalımı takip edebilirsiniz...

C#’ta async – await İkilisiyle ASenkron İşlemler

Merhaba

Asenkron mimari dendiği vakit benim aklıma Delegate(Delege) yapıları ve BeginInvoke metodları gelmektedir. Bunların dışında Asenkron işlemler için bir çok Thread yapısıyla beraber async ve await ikilisiylede asenkron çalışmalar gerçekleştirebiliriz. İşte bu içeriğimizde async – await ikilisini ele alacağız.

Öncelikle C# Framework’ü ile gelen hazır Async fonksiyonlarını ele alalım.

Örneğin;

        private void button1_Click(object sender, EventArgs e)
        {
            WebClient web = new WebClient();
            string Gelen = web.DownloadString(new Uri("http://www.gencayyildiz.com"));
            MessageBox.Show(Gelen);
        }

yukarıda gördüğünüz gibi WebClient sınıfını ele alalım. DownloadString metoduna verdiğimiz url adresin tüm kaynak komutlarını string olarak elde etmemizi sağlamaktadır. Bu komutu denediğiniz zaman hafif bir bekleme süresi olduğunu göreceksiniz. Bunun sebebi şuanda senkron bir yaklaşım sergilememizdir.

C#'ta async - await İkilisiyle ASenkron İşlemler

Dikkat ederseniz her metodun ayriyetten Async versiyonuda mevcuttur. Biliniz ki, .NET Framework’de yapılandırılmış çoğu sınıfın metodlarının Async versiyonları olacaktır. Peki ne işe yaramaktadır bu Async uzantılı versiyonlar? diye sorarsanız eğer yapılan işlemin asenkron temelli ele alınmasını sağlamaktadırlar.

Şimdi yukarıdaki örnek kodumuzu asenkron olarak ele alalım.

        private async void button1_Click(object sender, EventArgs e)
        {
            WebClient web = new WebClient();
            string Gelen = await web.DownloadStringTaskAsync(new Uri("http://www.gencayyildiz.com"));
            MessageBox.Show(Gelen);
        }

DownloadString metodunun DownloadStringTaskAsync versiyonunu kullanırsak yaptığımız işlem asenkron olarak gerçekleştirilecektir.
Amaaa…
Dikkat ederseniz bu işlemin asenkron olabilmesi için async ve await komutlarıyla üzerinde çalıştığımız metodu tazelememiz gerekmektedir.

O halde neler yaptık? detaylı konuşalım.
DownloadStringTaskAsync metodunu await komutuyla işaretleyerek verilen url adresindeki kaynağı string tipinde asenkron olarak elde etmek istedik. Bu işlemi gerçekleştirebilmek içinde çalıştığımız metodu async komutu ile işaretledik.

Bu demek oluyor ki,

  • async; içerisinde asenkron işlem yapılacak metodu belirtir. Benzer ifadeyle, içerisinde asenkron işlem yapacağımız metodu async keywordü ile işaretlemeliyiz.
  • Yukarıdaki örnekte gördüğünüz gibi olaylarda(event) dahil olmak üzere içerisinde asenkron işlem yapılacak tüm metodlar async ile işaretlenmek zorundadır.
  • asnyc ile işaretlenmiş bir metodda asenkron çalışacak komutlar await ile işaretlenir.
  • async ile işaretlenmiş metodun geri dönüş tipi; void, Task veyahut Task<T> geri dönüş tiplerinde olmalıdır.
  • await; sadece async ile işaretlenmiş metodlarda kullanılabilir.
  • async ile işaretlenmiş bir metod birden fazla await kullanabilir.

şeklinde genelleme yapabiliriz.

Peki, bir asenkron metod nasıl oluşturulur? sorusuna gelirsek…
Unutmayın ki, async ve await keywordleriyle kendi metodlarınızda asenkron bir yaklaşım uygulamak istiyorsanız Task sınıfını aklınızdan çıkarmayacaksınız.

Örnek olarak,

        void X() {
            int Sayac = 1;
            while (true)
            {
                label1.Text = Sayac.ToString();
                Sayac++;
            }
        }

senkron metodunu ele alırsak, asenkron yapmak için aşağıdaki gibi çalışacağız.

        Task X()
        {
            return Task.Run(() =>
            {
                int Sayac = 1;
                while (true)
                {
                    label1.Text = Sayac.ToString();
                    Sayac++;
                }
            });
        }

Örnektede gördüğünüz gibi void’e karşılık Task tipini kullanmaktayız. Bu asenkron işleme hazır oluşturulmuş metodun kullanımı aşağıdaki gibidir.

        private async void button1_Click(object sender, EventArgs e)
        {
            await X();
        }

Anlayacağınız await Task yahut Task<T> tipinden metodlara özgü keyworddür.

Şimdide geriye değer döndüren ve parametre alan bir metod üzerinde asenkron çalışmamızı gerçekleştirelim.

        Task<double> Don(double BaslangicDegeri, double BitisDegeri)
        {
            Task<double> islem = Task.Run<double>(() =>
            {
                while (true)
                {
                    if (BaslangicDegeri == BitisDegeri)
                        break;
                    label1.Text = BaslangicDegeri.ToString();
                    BaslangicDegeri++;
                }
                return BaslangicDegeri;
            });

            return islem;
        }

Gördüğünüz gibi Don metodunu asenkron olarak inşa edeceğimizden dolayı Task<double> tipinden değer dönmekte ve içerisinde Task.Run metodu ile gerekli işlemleri icra etmektedir. Son olarak Run metodu içerisinde döneceği double değeri return etmekte ve Don metodu komple Task<double> nesnesini geriye return etmektedir.

Kullanımı aşağıdaki gibidir.

        private async void button1_Click(object sender, EventArgs e)
        {
            MessageBox.Show((await Don(1000, 10000)).ToString());
        }

Sonucu asenkron bir işlem neticesinde mesaj kutusunda elde edilecektir.

Ve son olarak aşağıdaki kod bloğunu inceleyiniz.

        private async void button1_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Test1");
            MessageBox.Show((await Don(1000, 10000)).ToString());
            MessageBox.Show("Test2");
        }

Biraz önce yazmış olduğumuz Don isimli metod ile yukarıda bir işlem gerçekleştirdik. Bu olayı tetiklediğimiz zaman await metodu sadece Don metodunu gördüğünden dolayı asenkron işlem sade ve sadece Don metoduna uygulanacak, komple button1_Click isimli olay içerisinde ceyran etmeyecektir. Dolayısıyla, ekranda öncelikle “Test1”, ardından Don metodunun neticesi ve onunda ardından “Test2” değerlerini göreceğiz. Buradaki işlevleride komple asenkron bir şekilde gerçekleştirmek ve kod sırası gözetmeksizin bu komutlarıda kendi aralarında asenkron bir işlevde görmek istiyorsak aşağıdaki gibi bir çalışma yapmamız yeterli olacaktır.

        Task ASenkron()
        {
            return Task.Run(() =>
            {
                MessageBox.Show("Test1");
                Don(1000, 10000);
                MessageBox.Show("Test2");
            });
        }

Evet, bu işlemden sonra aşağıdaki gibi metodu çağırıp asenkron çalıştırabilirsiniz.

        private async void button1_Click(object sender, EventArgs e)
        {
            await ASenkron();
        }

async ve await komutları güzel ve pratik bir asenkron yapılandırması sunsada şahsen Delegate’lerin tadı ayrıydı benim için 🙂 Tabi ki de, herşeyi yeri ve zamanı gelince kullanmanın verimliliği o yapıya özel doruklarda yaşanmaktadır. O yüzden metodları bellek adreslerinden temsil edip asenkron işlev uygulamak istiyorsanız Delegate’leri, yok eğer temel sınıflarınızda asenkron işlev uygulayacaksanız tabi ki de async ve await komutlarını kullanmak en makuludur.

Okuduğunuz için teşekkür ederim…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar dilerim…

Bunlar da hoşunuza gidebilir...

20 Cevaplar

  1. ŞENEL YILDIZ dedi ki:

    Teşekkürler açıklayıcı bir makale olmuş

  2. derdim yok dedi ki:

    çok karmaşık anlatılmış 15 dk okudum hiçbirşey anlamadım.neyin ne işe yaradığını nasıl çalıştığını 3 cümleyle anlatamamışsın.seo kasma derdinden oluyor hep bunlar.böyle oluncada böyle gereksiz yazılar oluşuyor tabiiki.

    • Gençay dedi ki:

      Medeni cesaretin olup adını soyadını yazsanda IP’den kim olduğunu bulmam için beni yormasan 😉

      Neyse, yinede eleştirin için teşekkür ederim…
      Görüşürüz.

  3. Can dedi ki:

    Elinize sağlık. Gerçekten basit ve ileri seviye bir anlatım olmuş.

    Bu arada yukarıdaki arkadaşa bakmayın. beginner seviye arkadaşların anlamamsı normaldir. daha matematiksel işlemleri bile yapamayan insanlar. async-await ile ilgili feedback yapıyor

    • Gençay dedi ki:

      Teşekkür ederim 🙂
      Tabi her türlü eleştiriye açığız lakin taş üstüne taş koyan insanlardan gelene amadeyiz… Böyle sırf bi yerden nem kapıp sonra makale üzerinden salça olanlara değil. Keza yukarıdaki arkadaşın kim olduğunu kendisinin bildiği kadar bende biliyorum. ‘Eğitim’ esnasında kendisine ilgili mesajı da vermiş bulunmaktayım 😉 O anladı 🙂

      Faydalandıysanız ne mutlu…
      Kolay gelsin.

  4. baran dedi ki:

    Öncelikle anlatımınız için teşekkürler.bir şeyi merak ediyorum, aynı fonksiyon içinde birden fazla işlem yapacağım ama bu yapacağım işlemler birbirine bağlı. Yani birinin sırası bitmeden diğrine geçerse uygulama patlar databsea kayıt yapılmaz. Asenkron programlamada aynı metodun içerisinde yapılan işlemler yine sırasıyla mı yapılır? Benim anladığım aynı metodun içerisindeki işlemler await keywordleri sayesinde sırayla yapılır lakin o işlem yapılırken uygulama farklı metoda gider ve oradaki işlemleri de yapabilir aynı zamanda, değil mi? Örnek vermek gerekirse para havale işleminde önce hesaptan bakiye düşülür, sonra para havale edilir. Bu 2 işlemi de aynı metod içerisinde düşünürseniz bu işlerin sıryala yapılması gerekiyor.

    • Gençay dedi ki:

      Merhaba,

      async ve await keywordleriyle yapılan tasarımlarda, ana thread’i bloklamadan asenkron çalışan kodlar inşa edebiliyor ve bu kodları biryandan da senkronizasyonel bir şekilde takip edebiliyoruz.

      …benim anladığım aynı metodun içerisindeki işlemler await keywordleri sayesinde sırayla yapılır lakin o işlem yapılırken uygulama farklı metoda gider ve oradaki işlemleri de yapabilir aynı zamanda, değil mi…”

      Evet, dediğinizde haklısınız… Yukarıda bahsettiğim gibi ana thread bloklanmadan başka işlemlere açık await ile asenkron işlemler yapabiliyorsunuz lakin await kullanmaksızın bu süreci topyekün asenkron hale getirebilirsiniz. Yani bir metodun içerisinde Task dönen bir işlemi başlattığınızda bunu await ile beklemeden işlerseniz, metot akışa devam ederken kodlar asenkron olarak arkada işlenecektir. await ile bu asenkron olan ve ana thread’i bloklamayan işlemden sonucu bekleyebilmektesiniz.

      Verdiğiniz örnek çok yerinde olacaktır. Bir kullanıcıya özel banka işlemi yapılırken diğer kullanıcıları bloklamadan işlemlerin asenkron gerçekleştirilmesi gerekirken, işlemler içerisinde önce havale ve ardından bakiyeden düşüş gibi senkron durumlar söz konusu olacaktır.

      Sevgiler…

  5. baran dedi ki:

    Ütteki yorumumun devamı: Havale işlemi için düğmeye basıldı ve aynı zamanda başka birisi de hesabından para çekmek istedi ve para çekme metodu da çalıştı. Normalde havale işlemi bitmeden para çekme işlemi yaptırılmaz senkron programlamada ama asenkron programlamada bu yapılıyor. Olayın bu kısmını anladım ama öğrenmek istediğim kısım dediğim gibi havale metodunun içindeki işlemler de yine sırayla yapılıyor yani değil mi?

    • Gençay dedi ki:

      Havale işlemini yürüten metodunda nasıl tasarlandığına bağlıdır. Eğer asenkron işleme tabi tutulması söz konusuysa asenkron çalışacaktır, yok eğer senkron bir yaklaşım varsa senkron. Bu durumda olay ilgili metot seviyesinde ihtiyaca binaen düşünülmelidir.

  6. baran dedi ki:

    Çok bilgilendirici konular yazıyorsunuz tekrardan teşekkürler…(sebepsiz boş yere ayrılacaksan)

  7. ahmet dedi ki:

    Merhabalar,
    Ben istek ile sitelerin açık olup olmadığını kontrol eden bi uygulama üzerinde çalışıyorum. Asenkron bi method yapıp cevaplarını beklemesinin önüne geçmek istedim. İstekleri çok hızlı bir şekilde atıyor fakat geri dönüşleri nasıl işleyeceğimi, nasıl yakalayacağımı bilemediğimden “başarısız” mesajından başka bişey göremiyorum 🙂 sanırım konuyu tam anlayamadım.
    Bu konuda yönlendirebilir misiniz?

  8. hüseyin dedi ki:

    Merhaba anlatım için teşekkürler, uygulamamda anlattığınıza benzer ekledim. Çok fazla mail atan bir uygulama ve günlük joblarla da toplu mailler gitmekte. Burda tasklar serverlardaki ramlere yük bindirir mi ? Ya da mail yapısı için bir tavsiyeniz var mı ?

    private async void SendMailAsync(MailMessage message)
    {
        await SendMailFunc();
    }
    
    private Task SendMailFunc(object stateObject)
    {
        return Task.Run(() =>
        {
            ....bla bla lba
            smtp.SendAsync(NetMail, NetMail.To);
        });
    }
    
    • Gençay dedi ki:

      Merhaba,

      Lüzumsuz kullanılan her bir Task Ram’e yük bindirir. Yukarıdaki örnek çalışmanızda ‘SendAsnyc’i ayrı bir Task içerisinde çalıştırmaktansa, direkt olarak ‘SendMailFunc’ içerisinde asenkron çalıştırmanı öneririm. Şöyle ki;

      private async void SendMailAsync(MailMessage message)
      {
          await SendMailFunc();
      }
       
      private async Task SendMailFunc(object stateObject)
      {
          await smtp.SendAsync(NetMail, NetMail.To);
      }
      

      Kolay gelsin…

  1. 24 Eylül 2020

    […] async ile işaretlenmiş bir metod birden fazla await kullanabilir. daha fazla bilgi için tıklayınız […]

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

*