Merhabalar. Bu makalemde hayatımıza C# 5.0 ile beraber gelen asenkron programlama muhabbetinden bahsedeceğim. Teoriyi anlatıp bir örnekle pratiğe dökmeye çalışacağım.

Asenkron Programlama Nedir?

Önce bir cümleyle açıklayalım. Asenkron programlama, işin parçalara ayrılıp tüm işlemlerin aynı anda sürdürülmesini sağlar. Asenkron Programlama ile programımız içerisinde yazdığımız bir kod işletilirken, aynı program içerisinde diğer kodlar da işletilebilir. Bu sayede kullanıcı programımızın bir bölümünü kullanırken, başka bir bölümü ile de işlem yapabilir. Bu özellikle mobil uygulamalar için hayati bir önem taşıyor. Mobil haricinde de birçok kullanım alanı var.

Asenkron programlama, multi threading ile karıştırılmamalıdır. Asenkron olarak yazacağımız kodlar tek bir thread üzerinde de çalışabilir. Asenkron programlamanın özelliği farklı thread’lerde çalışması değil, verdiğimiz işi parçalara ayırarak programımız üzerinde birden fazla işin aynı anda yürütülmesini sağlamaktır.

Asenkron Programlamada Async ve Await Anahtar Kelimeleri

C# 5.0 ile gelen asenkron programlama tekniğini kullanmak için async ve await anahtar kelimlerine ihtiyaç duyuyoruz. Normal olarak çalışan bir metodu asenkron hale getirmek istiyorsak, metodu tanımlarken async kullanırız daha sonrasında ise kullanacağımız kod satırının önüne await anahtar kelimesini getiririz. Metodu tanımlarken async kullanmamızın sebebi bu yazdığımız metod içerisinde asenkron kod parçaları yazacağımızı CLR’a bildirmemiz gerektiğinden kaynaklanıyor. Await’i de asenkron olarak çalışmasını istediğimiz kodları seçiyoruz.

Örnekleri Windows Form Application üzerinde göstereceğim. Öncelikle siz de yeni bir tane Windows Form Application oluşturun. Hemen aşağıda form görüntüsünü paylaşayım.

3 tane farklı örnek yapacağım. Birincisinde Asenkron olarak bir web sayfasının html kodlarını bir string’e çekip mesaj olarak göstereceğim. Bunu senkron ve Asenkron olarak 2 farklı şeklide yapacağım.

İkincisinde yine asenkron ve senkron olmak üzere bir picturebox’a internetten resim alacağım.

Üçüncü örnekte ise sadece bir mesaj göstereceğim. Ancak bu mesajdan önce thread’i uykuya alacağım. Bu süre içerisinde senkron yazdığımız kodlar işlenirken arayüzün çalışmadığını ancak asenkron kodlar işlenirken arayüzün hiçbir şekilde aksamaya uğramadığını göreceksiniz. Başlayalım.

Birinci örnekten başlayalım. Kodlarını aşağıda vereyim ve açıklamasını yapayım.

        private void btn_NormalString_Click(object sender, EventArgs e)
        {
            DownloadData(); //DownloadData metodunu çağırıyoruz...
        }

        private async void btn_AsyncString_Click(object sender, EventArgs e) //Async keyword'üne dikkat!
        {
            string getdata = await DownloadDataAsync(); //await keyword'üne dikkat!
            MessageBox.Show(getdata);
        }

        private void DownloadData()
        {
            WebClient wc = new WebClient(); //WebClient Sınıfından yeni bir WebClient nesnesi türetiliyor...
            string data = wc.DownloadString("http://w3schools.com");
            //Ürettiğimiz wc isimli nesnenin DownloadString metoduyla w3schools kaynağındaki html kodlarını çekip değişkene aktarıyoruz
            MessageBox.Show("a");
        }

        async Task<string> DownloadDataAsync() //DownloadDataAsync Task'inin asenkron olduğunu async keyword'ü ile belirtiyoruz. Asenkron kullanmak istediğimiz için Task oluşturduk.
        {
            WebClient wc = new WebClient(); //WebClient Sınıfından yeni bir WebClient nesnesi türetiliyor...
            string data = await wc.DownloadStringTaskAsync("http://w3schools.com");
            //Ürettiğimiz wc isimli nesnenin DownloadString metoduyla w3schools kaynağındaki html kodlarını çekip değişkene aktarıyoruz
            return data; //Veri kaynağından çektiği verileri tekrar döndürüyor. Böylece bu metodu çağıran yere bu bilgi ulaşmış oluyor.
        }

Kodların açıklama satırlarında her kodun işlevini tek tek yazmaya çalıştım. Genel olarak anlatırsam 2 tane işlenecek metod var. Birisi DownloadData olan standart metod. Diğeri ise DownloadAsyncData olan asenkron metod. İkisi de aynı işi yapıyor. Ancak iş yapılırken oluşan farka gelecek olursak, senkron olanda kullanıcı işlem yapılırken uygulamamız içerisinde başka hiçbir işlem yapamıyor. Asenkron olarak yürütülen işlemde ise kullanıcı uygulama içerisinde başka işlemler de yapabiliyor. Asenkron programlamanın amacı da budur. Kimse kum saati görmek istemez.

İkinci örneğin kodları :

    private void btn_NormalPicture_Click(object sender, EventArgs e) //Normal metod.
    {
    pictureBox1.Load(“https://i0.wp.com/adviceal.com/teknoloji/wp-content/uploads/2019/09/code.jpg?fit=2000%2C1335&ssl=1”);
    }

    private void btn_AsyncPicture_Click(object sender, EventArgs e)
    {
    pictureBox1.LoadAsync(“https://i0.wp.com/adviceal.com/teknoloji/wp-content/uploads/2019/09/code.jpg?fit=2000%2C1335&ssl=1”); //Hazır bir async metod.
    }

Bu örnekte async/await yok. Ancak hazır bir asenkron metod olduğu için aldım. Temel olarak yine aynı işi yapıyor. Bunun gibi hazır asenkron metodlar da .Net Framework içerisine yerleştirilmiş durumda.

Üçüncü örneğimiz ise bu konuyu araştırırken internette gördüğüm klasik bir örnek. Thread.Sleep metodu kullanarak kullanılan thread bir süreliğine donduruluyor. Ve program asenkron ve senkron olarak bu kodları işletirken, programın verdiği tepkilere bakılıyor. Tabi ki senkron metod işletilirken yine programa müdahale edilemiyor. Ancak senkron metod işletilirken sıkıntı yok. Yine yorum satırlarında açıklama yapmaya çalıştım.

        private void btn_NormalString_Click(object sender, EventArgs e)
        {
            DownloadData(); //DownloadData metodunu çağırıyoruz...
        }

        private async void btn_AsyncString_Click(object sender, EventArgs e) //Async keyword'üne dikkat!
        {
            string getdata = await DownloadDataAsync(); //await keyword'üne dikkat!
            MessageBox.Show(getdata);
        }

        private void DownloadData()
        {
            WebClient wc = new WebClient(); //WebClient Sınıfından yeni bir WebClient nesnesi türetiliyor...
            string data = wc.DownloadString("http://w3schools.com");
            //Ürettiğimiz wc isimli nesnenin DownloadString metoduyla w3schools kaynağındaki html kodlarını çekip değişkene aktarıyoruz
            MessageBox.Show("a");

        }

        async Task<string> DownloadDataAsync() //DownloadDataAsync Task'inin asenkron olduğunu async keyword'ü ile belirtiyoruz. Asenkron kullanmak istediğimiz için Task oluşturduk.
        {
            WebClient wc = new WebClient(); //WebClient Sınıfından yeni bir WebClient nesnesi türetiliyor...
            string data = await wc.DownloadStringTaskAsync("http://w3schools.com");
            //Ürettiğimiz wc isimli nesnenin DownloadString metoduyla w3schools kaynağındaki html kodlarını çekip değişkene aktarıyoruz
            return data; //Veri kaynağından çektiği verileri tekrar döndürüyor. Böylece bu metodu çağıran yere bu bilgi ulaşmış oluyor.
        }

Asenkron metod tanımlarken bazı kurallara dikkat etmeniz gerekiyor. Örnek vermem gerekirse void ile beraber kullanılmaz. Geri dönüş tipi Task<TRESULT> olmak zorundadır.

Kodların Tamamı:

Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;  
using System.Text;
using System.Threading;  
using System.Threading.Tasks;
using System.Windows.Forms;

namespace AsyncSamples
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btn_NormalString_Click(object sender, EventArgs e)
        {
            DownloadData(); //DownloadData metodunu çağırıyoruz...
        }

        private async void btn_AsyncString_Click(object sender, EventArgs e) //Async keyword'üne dikkat!
        {
            string getdata = await DownloadDataAsync(); //await keyword'üne dikkat!
            MessageBox.Show(getdata);
        }

        private void DownloadData()
        {
            WebClient wc = new WebClient(); //WebClient Sınıfından yeni bir WebClient nesnesi türetiliyor...
            string data = wc.DownloadString("http://w3schools.com");
            //Ürettiğimiz wc isimli nesnenin DownloadString metoduyla w3schools kaynağındaki html kodlarını çekip değişkene aktarıyoruz
            MessageBox.Show("a");
        }

        async Task<string> DownloadDataAsync() //DownloadDataAsync Task'inin asenkron olduğunu async keyword'ü ile belirtiyoruz. Asenkron kullanmak istediğimiz için Task oluşturduk.
        {
            WebClient wc = new WebClient(); //WebClient Sınıfından yeni bir WebClient nesnesi türetiliyor...
            string data = await wc.DownloadStringTaskAsync("http://w3schools.com");
            //Ürettiğimiz wc isimli nesnenin DownloadString metoduyla w3schools kaynağındaki html kodlarını çekip değişkene aktarıyoruz
            return data; //Veri kaynağından çektiği verileri tekrar döndürüyor. Böylece bu metodu çağıran yere bu bilgi ulaşmış oluyor.
        }

        private void btn_NormalPicture_Click(object sender, EventArgs e) //Normal metod.
        {
            pictureBox1.Load("https://i0.wp.com/adviceal.com/teknoloji/wp-content/uploads/2019/09/code.jpg?fit=2000%2C1335&ssl=1");
        }

        private void btn_AsyncPicture_Click(object sender, EventArgs e)
        {
            pictureBox1.LoadAsync("https://i0.wp.com/adviceal.com/teknoloji/wp-content/uploads/2019/09/code.jpg?fit=2000%2C1335&ssl=1"); //Hazır bir async metod.
        }

        private void btn_NormalSleep_Click(object sender, EventArgs e)
        {
            NormalMessage(); //NormalMessage metodunu çağırıyoruz...
        }

        private async void btn_AsyncSleep_Click(object sender, EventArgs e)
        {
            await ShowAsyncMessage(); //ShowAsyncMessage metodunu asenkron olarak çağırıyoruz...
        }

        private async Task ShowAsyncMessage() //async keyword'ü ile yeni bir Task türetiyoruz...
        {
            await Task.Run(() => Thread.Sleep(5000)); //5000 milisaniye işlemi durduruyoruz...



            MessageBox.Show("İşlem Tamamlandı");
        }

        private void NormalMessage() //Normal mesaj gösteren metod
        {
            Thread.Sleep(5000);
            MessageBox.Show("İşlem Tamamlandı");
        }

    }
}

Form1.Designer.cs

namespace AsyncSamples
{
    partial class Form1
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.btnNormalString = new System.Windows.Forms.Button();
            this.btnAsyncString = new System.Windows.Forms.Button();
            this.groupBox2 = new System.Windows.Forms.GroupBox();
            this.pictureBox1 = new System.Windows.Forms.PictureBox();
            this.btnNormalPicture = new System.Windows.Forms.Button();
            this.btnAsyncPicture = new System.Windows.Forms.Button();
            this.groupBox1 = new System.Windows.Forms.GroupBox();
            this.groupBox3 = new System.Windows.Forms.GroupBox();
            this.btnNormalSleep = new System.Windows.Forms.Button();
            this.btnAsyncSleep = new System.Windows.Forms.Button();
            this.pictureBox2 = new System.Windows.Forms.PictureBox();
            this.groupBox2.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
            this.groupBox1.SuspendLayout();
            this.groupBox3.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).BeginInit();
            this.SuspendLayout();
            // 
            // btnNormalString
            // 
            this.btnNormalString.Location = new System.Drawing.Point(8, 23);
            this.btnNormalString.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
            this.btnNormalString.Name = "btnNormalString";
            this.btnNormalString.Size = new System.Drawing.Size(363, 28);
            this.btnNormalString.TabIndex = 0;
            this.btnNormalString.Text = "Senkron Olarak Çağır";
            this.btnNormalString.UseVisualStyleBackColor = true;
            this.btnNormalString.Click += new System.EventHandler(this.btn_NormalString_Click);
            // 
            // btnAsyncString
            // 
            this.btnAsyncString.Location = new System.Drawing.Point(8, 59);
            this.btnAsyncString.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
            this.btnAsyncString.Name = "btnAsyncString";
            this.btnAsyncString.Size = new System.Drawing.Size(363, 28);
            this.btnAsyncString.TabIndex = 1;
            this.btnAsyncString.Text = "Asenkron Olarak Çağır";
            this.btnAsyncString.UseVisualStyleBackColor = true;
            this.btnAsyncString.Click += new System.EventHandler(this.btn_AsyncString_Click);
            // 
            // groupBox2
            // 
            this.groupBox2.Controls.Add(this.pictureBox1);
            this.groupBox2.Controls.Add(this.btnNormalPicture);
            this.groupBox2.Controls.Add(this.btnAsyncPicture);
            this.groupBox2.Location = new System.Drawing.Point(36, 137);
            this.groupBox2.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
            this.groupBox2.Name = "groupBox2";
            this.groupBox2.Padding = new System.Windows.Forms.Padding(4, 4, 4, 4);
            this.groupBox2.Size = new System.Drawing.Size(371, 267);
            this.groupBox2.TabIndex = 5;
            this.groupBox2.TabStop = false;
            this.groupBox2.Text = "Asenkron Olarak Resim İsteme";
            // 
            // pictureBox1
            // 
            this.pictureBox1.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom;
            this.pictureBox1.Location = new System.Drawing.Point(9, 25);
            this.pictureBox1.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
            this.pictureBox1.Name = "pictureBox1";
            this.pictureBox1.Size = new System.Drawing.Size(361, 166);
            this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
            this.pictureBox1.TabIndex = 2;
            this.pictureBox1.TabStop = false;
            // 
            // btnNormalPicture
            // 
            this.btnNormalPicture.Location = new System.Drawing.Point(7, 198);
            this.btnNormalPicture.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
            this.btnNormalPicture.Name = "btnNormalPicture";
            this.btnNormalPicture.Size = new System.Drawing.Size(363, 28);
            this.btnNormalPicture.TabIndex = 0;
            this.btnNormalPicture.Text = "Senkron Olarak Çağır";
            this.btnNormalPicture.UseVisualStyleBackColor = true;
            this.btnNormalPicture.Click += new System.EventHandler(this.btn_NormalPicture_Click);
            // 
            // btnAsyncPicture
            // 
            this.btnAsyncPicture.Location = new System.Drawing.Point(7, 234);
            this.btnAsyncPicture.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
            this.btnAsyncPicture.Name = "btnAsyncPicture";
            this.btnAsyncPicture.Size = new System.Drawing.Size(363, 28);
            this.btnAsyncPicture.TabIndex = 1;
            this.btnAsyncPicture.Text = "Asenkron Olarak Çağır";
            this.btnAsyncPicture.UseVisualStyleBackColor = true;
            this.btnAsyncPicture.Click += new System.EventHandler(this.btn_AsyncPicture_Click);
            // 
            // groupBox1
            // 
            this.groupBox1.Controls.Add(this.btnNormalString);
            this.groupBox1.Controls.Add(this.btnAsyncString);
            this.groupBox1.Location = new System.Drawing.Point(36, 25);
            this.groupBox1.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
            this.groupBox1.Name = "groupBox1";
            this.groupBox1.Padding = new System.Windows.Forms.Padding(4, 4, 4, 4);
            this.groupBox1.Size = new System.Drawing.Size(376, 98);
            this.groupBox1.TabIndex = 4;
            this.groupBox1.TabStop = false;
            this.groupBox1.Text = "Asenkron/Senkron olarak StringVeri İsteme";
            // 
            // groupBox3
            // 
            this.groupBox3.Controls.Add(this.btnNormalSleep);
            this.groupBox3.Controls.Add(this.btnAsyncSleep);
            this.groupBox3.Location = new System.Drawing.Point(31, 422);
            this.groupBox3.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
            this.groupBox3.Name = "groupBox3";
            this.groupBox3.Padding = new System.Windows.Forms.Padding(4, 4, 4, 4);
            this.groupBox3.Size = new System.Drawing.Size(376, 98);
            this.groupBox3.TabIndex = 6;
            this.groupBox3.TabStop = false;
            this.groupBox3.Text = "Asenkron/Senkron Thread.Sleep Uygulama";
            // 
            // btnNormalSleep
            // 
            this.btnNormalSleep.Location = new System.Drawing.Point(8, 23);
            this.btnNormalSleep.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
            this.btnNormalSleep.Name = "btnNormalSleep";
            this.btnNormalSleep.Size = new System.Drawing.Size(363, 28);
            this.btnNormalSleep.TabIndex = 0;
            this.btnNormalSleep.Text = "Senkron Olarak Çağır";
            this.btnNormalSleep.UseVisualStyleBackColor = true;
            this.btnNormalSleep.Click += new System.EventHandler(this.btn_NormalSleep_Click);
            // 
            // btnAsyncSleep
            // 
            this.btnAsyncSleep.Location = new System.Drawing.Point(8, 59);
            this.btnAsyncSleep.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
            this.btnAsyncSleep.Name = "btnAsyncSleep";
            this.btnAsyncSleep.Size = new System.Drawing.Size(363, 28);
            this.btnAsyncSleep.TabIndex = 1;
            this.btnAsyncSleep.Text = "Asenkron Olarak Çağır";
            this.btnAsyncSleep.UseVisualStyleBackColor = true;
            this.btnAsyncSleep.Click += new System.EventHandler(this.btn_AsyncSleep_Click);
            // 
            // pictureBox2
            // 
            this.pictureBox2.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom;
            this.pictureBox2.Image = global::AsyncSamples.Properties.Resources.penguifall_1_;
            this.pictureBox2.Location = new System.Drawing.Point(415, 161);
            this.pictureBox2.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
            this.pictureBox2.Name = "pictureBox2";
            this.pictureBox2.Size = new System.Drawing.Size(269, 166);
            this.pictureBox2.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
            this.pictureBox2.TabIndex = 3;
            this.pictureBox2.TabStop = false;
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(700, 545);
            this.Controls.Add(this.pictureBox2);
            this.Controls.Add(this.groupBox2);
            this.Controls.Add(this.groupBox1);
            this.Controls.Add(this.groupBox3);
            this.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
            this.Name = "Form1";
            this.Text = "Asenkron/Senkron Örnekler";
            this.groupBox2.ResumeLayout(false);
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
            this.groupBox1.ResumeLayout(false);
            this.groupBox3.ResumeLayout(false);
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).EndInit();
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.Button btnNormalString;
        private System.Windows.Forms.Button btnAsyncString;
        private System.Windows.Forms.GroupBox groupBox2;
        private System.Windows.Forms.PictureBox pictureBox1;
        private System.Windows.Forms.Button btnNormalPicture;
        private System.Windows.Forms.Button btnAsyncPicture;
        private System.Windows.Forms.GroupBox groupBox1;
        private System.Windows.Forms.GroupBox groupBox3;
        private System.Windows.Forms.Button btnNormalSleep;
        private System.Windows.Forms.Button btnAsyncSleep;
        private System.Windows.Forms.PictureBox pictureBox2;
    }
}