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...

30 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.

  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…

  9. İsa dedi ki:

    Merhabalar Gencay abi. Abi şöyle bir durumla karsı karsıyayım. Benım polly yı kullandıgım yer asyn olmayan bir metot. Fakat bu metotun ıcerısınde polly ı asyn olarak çalıştırmam lazım. bu konu hakkında bir fikrin var mı nasıl bir yol izleyebılırım.

    • Gençay dedi ki:

      Merhaba İsa,

      Yani öyle bi yazmışsın ki, Polly’i async kullanman gerekiyorsa kullan o zaman diyesim geldi 🙂 Yani örnek kod parçası paylaşsan daha rahat istişare edebileceğiz ama gerek görmemişsin anlaşılan. Bende gerek gördüğüm kadarıyla cevaplayayım bari 🙂 Anladığım kadarıyla Polly’nin async fonksiyonlarını, async olmayan kendi fonksiyonunda kullanmak istiyorsun. Bunun için üç işlem yürütebilirsin;

      • 1. İlgili metodunu async yapabilirsin. (ki bu başta da söylediğim gibi direkt yazdıklarından bunu diyesi gelir insanın)
      • 2. Yok illa metodum async olmayacak, başka yerlerle bağlılığım var diyorsan o zaman Polly’i async olan başka bir fonksiyonda tetikler, ilgili fonksiyon içerisinde yeni fonksiyonu çağırarak .Result ile ekstra işleme gerek kalmaksızın bekleyebilir ve sonucunu elde edebilirsin.
      • 3. Metot imzasında herhangi bir async işlem yapmaksızın polly’nin ilgili async fonksiyonunu .Result ile bekleyebilirsin.

      Umarım faydalı olur 🙂

      Kolay gelsin…

      • isa dedi ki:

        abi teşekkur ederım. abı kod parcacıgını almam yasak oldugu ıcın ekleyemedım. Kusura bakma lutfen. Ancak dediğin 2. ve 3. durumu yapmaya calısacagım. Geri bildirimin için teşekkur ederım abi. Sağolasın:)

        • Gençay dedi ki:

          Yasak olan durumlarda, durumu yansıtan muadil bir örnek kod parçası oluşturup paylaşabilirsin. O zaman kimse bişey diyemez 🙂

          Kolay gelsin…

  10. onur dedi ki:

    hocam bilgi için teşekkürler, merak ettiğim konu task yeni bir thread mi oluşturuyor ?

  11. BD dedi ki:

    Merhaba,
    Öncelikle anlatım için çok teşekkürler. Başlangıç seviyesindeyim ve şunu merak ediyorum : Bir metot içerisindeki komutların döngüler vs kısmen yukarıdan aşağı çalıştığını düşünürsek bir satırdaki işlem bitmeden diğerine zaten geçmemesi gerekmez mi? Benim burada await ile işaretlediğim iş diyelim ki uzun süreli bir iş ve sonrasında gelen bir çok kod var. Yani bekleme olmaması için await ile işaretlediğim metodun işi bitmeden sıradaki komutları çalıştırması anlamına mı geliyor bu durum? Öyleyse mantıksal bir hata oluşmaz mı? Sanırım fazlaca aklım karıştı 🙂
    Cevap için şimdiden teşekkürler.

    • Gençay dedi ki:

      Merhaba,

      Senkron çalışma mantığına göre dediğiniz gibi sıranın bozulmaması gerekmekte ve bir işlem bitmeden başka bir işleme başlamaması gerekmektedir. await‘in kullanım gereksinimi aşağıdaki gibi bir senaryodan kaynaklanmaktadır.

      X();
      Y();
      Z();
      

      Yukarıdaki kod bloğunu ele alırsak eğer, X fonksiyonunun işlevi bitmeksizin Y fonksiyonu başlamayacak aynı şekilde Y fonksiyonunun işlemi bitmeksizin Z fonksiyonu başlamayacaktır. Evet, bu gayet doğaldır. Lakin burada dikkat etmemiz gereken bir husus vardır ki, bu metotların her biri sistemin/uygulamanın ana Thread’ini bloklayacaklardır. Yani büyük bir sistemin herhangi bir noktasındaki X, Y ya da Z fonksiyonlarından biri çalışırken o sistem bu fonksiyonun bitmesini bekleyecektir. İşte burada await devreye girecek ve ana Thread’i bloklamadan sadece ilgili metodun senkron olarak beklenmesini sağlayacaktır. Şöyle ki:

      await X();
      await Y();
      await Z();
      

      Yukarıdaki kod bloğuna bakarsak eğer; X, Y veya Z fark etmeksizin yine kendi aralarında senkron çalışacaktırlar. Lakin farklı thread’de çalıştırılacakları için uygulamanın ana Thread’ini bloklamayacaklarından dolayı asenkron bir çalışma sergileyecektir. Burada await keyword’ü ilgili fonksiyona : -sen asenkron çalış ama seni burada senkron olarak beklemekteyim- demektedir. Böylece ilgili metot, yukarıda da ifade etmeye çalıştığım gibi farklı bir Thread’de asenkron olarak işlenirken, tetiklendiği yer senkron olarak beklemede kalacaktır. Böylece uygulama sistemdeki X, Y veya Z fonksiyonları için bekleme yapmayacak ve kullanıcı dostu bir süreç yaşanacaktır.

      Umarım anlatabilmişimdir…
      Sevgiler…

  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