Makale Özeti

Uygulamalarımızın genel mantığı bellekteki bir işlemin CPU adını verdiğimiz işlemciler aracılığı ile belirlenmiş hiyerarşik bir sırada çalışması yönündedir...

Makale

Uygulamalarımızın genel mantığı bellekteki bir işlemin CPU adını verdiğimiz işlemciler aracılığı ile belirlenmiş hiyerarşik bir sırada çalışması yönündedir. Böyle bir yapıda çalışan 5 farklı processinizin olduğunu ve her bir processin 10 msn'ye çalışacağını varsayarsak, işlemleriniz aşağıdaki akışta gerçekleşecektir:

- 1. Process çalışmaya başlar (10 msn çalıştırılır, bellekten silinir)
- 2. Process çalışmaya başlar (10 msn çalıştırılır, bellekten silinir)
- 3. Process çalışmaya başlar (10 msn çalıştırılır, bellekten silinir)
- 4. Process çalışmaya başlar (10 msn çalıştırılır, bellekten silinir)
- 5. Process çalışmaya başlar (10 msn çalıştırılır, bellekten silinir)


Oysa Microsoft Windows gibi işletim sistemlerine baktığımızda aynı anda Windows Media Player'dan müzik dinleyebilmekte, Microsoft Office Outlook ile maillerimi okuyabilmekte ve Microsoft Internet Explorer ile internette dolaşabilmekteyim. Yani aynı anda birden fazla processi çalıştırabilmekteyim.

Nasıl oluyor da aynı anda birden fazla process çalıştırabiliyoruz? Terim olarak aynı anda gerçekleşmeyi seçsekde aslında aynı anda gerçekleşen herhangi bir process bulunmuyor. Olayın özü yapmaya çalıştığınız işlerin küçük iş parçacıklarına yani thread lere bölünmüş olmasında. Uygulamanızı thread mimarisinde hazırladığınızda olağan process olan önce ilk process sonra diğerlerini hiyerarşik sırayla tamamlama algoritması, iş parçacıklarınızı karışık bir sıra ile çalıştırmaya ve sanki tüm process ler aynı anda gerçekleşiyormuş gibi algılamanıza dönüşüyor.

Yani işlem akışınızı aşağıdaki gibi değiştiriliyor:

- 1. Process'in ilk parçacığı çalışmaya başlar
- 2. Process'in ilk parçacığı çalışmaya başlar
- 3. Process'in ilk parçacığı çalışmaya başlar
- 4. Process'in ilk parçacığı çalışmaya başlar
- 5. Process'in ilk parçacığı çalışmaya başlar
- 1. Process'in ikinci parçacığı çalışmaya başlar
- 2. Process'in ikinci parçacığı çalışmaya başlar
- 3. Process'in ikinci parçacığı çalışmaya başlar
- 4. Process'in ikinci parçacığı çalışmaya başlar
- 5. Process'in ikinci parçacığı çalışmaya başlar
- vs...

Bu işlemler o kadar hızlı gerçekleşir ki, aynı anda birden fazla uygulamayı çalıştırdığınızdan emin hale gelebilirsiniz.

Uygulama çalışma mimarisi hakkında detay bilgi almak isteyenler için http://www.yazgelistir.com/Makaleler/MakaleDetay.aspx?MakaleId=1000000250 adresindeki makaleyi önerebilirim.

Bu makalede örnek bir thread uygulaması yapacağız ve aşama aşama neyin  ne olduğu, neden olduğu yönünde bilgi vermeye çalışacağız. Görsel olarak thread mantığını algılayabilmemiz için örnekte progressbar bileşenini kullanacağız.

Windows Forms proje ekranımız aşağıdaki şekilde tasarlanacak;



Uygulamayı önce alışık olduğumuz mantıkta yaratacağız. Bu şekilde multithreading ile elde edeceğimiz performansı gözler önüne sermiş olacağız.

Alışa geldiğimiz mantıkta yazacağımız kod bloğu aşağıdaki şekilde olacaktır:

public void Ilerle1()
{
    for (int i = 0; i < 100; i++)
    {
         PB1.Value += 1;
        Thread.Sleep(50);
    }
}

public void Ilerle2()
{
    for (int i = 0; i < 100; i++)
    {
        PB2.Value += 1;
        Thread.Sleep(50);
    }
}

public void Ilerle3()
{
    for (int i = 0; i < 100; i++)
    {
        PB3.Value += 1;
        Thread.Sleep(50);
    }
}

public void Ilerle4()
{
    for (int i = 0; i < 100; i++)
    {
        PB4.Value += 1;
        Thread.Sleep(50);
    }
}

public void Ilerle5()
{
    for (int i = 0; i < 100; i++)
    {
        PB5.Value += 1;
        Thread.Sleep(50);
     }
}

private void btnBaslat_Click(object sender, EventArgs e)
{
    Ilerle1();
    Ilerle2();
    Ilerle3();
    Ilerle4();
    Ilerle5();
}


Yazdığımız kod bloğunu kısaca açıklayacak olursak; ProgressBar sınıfından parametre alan ve aldığı nesneyi 0'dan 100'e kadar bir döngüye sokarak value değerini artıran bir metodum var. bu metod progressbarın ilerlemesini sağlıyor. Ve btnBaslat'ın click olayında Ilerle() metoduma 5 farklı progressbarımı gönderiyorum. Uygulamayı çalıştırdığınızda alacağınız ekran görüntüsü aşağıdaki şekilde olacak.



Gördüğünüz gibi önce 1.process çalıştı, onun tamamlanmasının ardından 2.processimiz çalışmaya başladı. Henüz 3., 4. ve 5.processlerimiz çalışmaya bile başlamadı. Aslında bu ekran görüntüsü bile söylemeye çalıştığımız şeyi anlatmak için yeterli.

Şimdi bu uygulamayı multithread bir hale getirelim ve vaadettiğimiz sonuca yani tüm processlerin aynı anda çalışmasına ulaşmaya çalışalım.

Uygulamayı multithread hale getirebilmek için öncelikle projenize System.Threading namespaceini eklemeniz gerekiyor. Bu sayede Thread ve ThreadStart isimli sınıflarımızı kullanmaya başlayabileceğiz.

Ilerle() metodunda herhangi bir değişiklik yapmayacağım çünkü yapmaya çalıştığım şey işin gerçekleşme mekanizmasını değil çalışma mekanizmasını değiştirmek. Bu nedenle tek yapacağım şey Ilerle() metodunun thread mimarisi kapsamında çalışmasını sağlamak olacak. Yani progressbarların ilerlemesini hiyerarşik önce gelene önce hizmet mantığından çıkarıp tüm processlerin aynı anda çalışmasını sağlamak olacak.

public void Ilerle1()
{
    for (int i = 0; i < 100; i++)
    {
         PB1.Value += 1;
        Thread.Sleep(50);
    }
}

public void Ilerle2()
{
    for (int i = 0; i < 100; i++)
    {
        PB2.Value += 1;
        Thread.Sleep(50);
    }
}

public void Ilerle3()
{
    for (int i = 0; i < 100; i++)
    {
        PB3.Value += 1;
        Thread.Sleep(50);
    }
}

public void Ilerle4()
{
    for (int i = 0; i < 100; i++)
    {
        PB4.Value += 1;
        Thread.Sleep(50);
    }
}

public void Ilerle5()
{
    for (int i = 0; i < 100; i++)
    {
        PB5.Value += 1;
        Thread.Sleep(50);
     }
}

//5 farklı metodumuzun her biri için bir thread nesnesi yaratıyoruz.
//Aynı nesneleri threadleri dudururken de kullanacağımız için global olarak tanımlıyoruz.

Thread T1, T2, T3, T4, T5;

private void btnBaslat_Click(object sender, EventArgs e)
{
    //Yarattığımız her bir thread'i ThreadStart sınıfından örnekliyoruz.
    //Her bir threadstart sınıfına parametre olarak çalıştıracağı metodları belirtiyoruz.

    T1 = new Thread(new ThreadStart(Ilerle1));
    T2 = new Thread(new ThreadStart(Ilerle2));
    T3 = new Thread(new ThreadStart(Ilerle3));
    T4 = new Thread(new ThreadStart(Ilerle4));
    T5 = new Thread(new ThreadStart(Ilerle5));
    //Tüm threadleri çalıştırıyoruz.
    T1.Start();
    T2.Start();
    T3.Start();
    T4.Start();
    T5.Start();
}



Ekran çıktısından da gördüğünüz üzere artık tüm iş parçacıklarımız ardarda çağrılıyor ve programımız aynı anda 5 farklı process i birden gerçekleştirebiliyor.

Şimdi çalışan threadlerimizi durdurma ve devam ettirme aşamasındayız. Threadleri durdurmak için Suspend() metodunu kullanacağız. Suspend() metodunu kullanarak durduracağınız threadleri Resume() metodunu kullanarak yeniden çalıştırmaya başlayabilirsiniz. Diğer bir thread durdurma metodu olan Abort() ise threadin tekrar başlatılamaz halde durdurulmasını sağlayacaktır.

Bu bilgiler ışığında "Beklet" ve "Devam Et" butonlarımızı programlayalım. Abort metodunun kullanımını gösterebilmek adına T1 isimli threadimizi Abort() metodunu kullanarak durduracağım. Bu sayede durdurduğumuz thread in Resume() metodu ile tekrar başlatılamadığını görmüş olacağız.

private void btn_Durdur_Click(object sender, EventArgs e)
{
    //T1 threadini tamamen diğer threadlerimizi ise sadece belli bir süre boyunca durduruyoruz.
    T1.Abort();
    T2.Suspend();
    T3.Suspend();
    T4.Suspend();
    T5.Suspend();
}

private void btn_DevamEt_Click(object sender, EventArgs e)
{
    //Tüm threadlerimizi yeniden çalıştırıyoruz.
    T2.Resume();
    T3.Resume();
    T4.Resume();
    T5.Resume();
    //T1 threadi Abort() ile kapatıldığından tekrar çalıştırılamaz
    //T1.Resume();

}


Durdur ve Devam Et butonları için yazdığımız kodlar sonucunda Durdur butonu tüm threadlerin beklemeye alınmasını sağlarken



Devam et butonu Abort() metodu ile tamamen yok edilen T1 threadi dışındaki tüm threadlerimizi kaldığı yerden devam ettiriyor.



Son olarak "Çıkış" butonumuz ile ilgilenelim. Çoğu zaman uygulamanız bir iş yaparken onu kapatmak istersiniz ve uygulamanın hiç şüphesiz hata ile size karşılık verir. Multithreading bir uygulamada da iş parçacıklarının tamamı tamamlanmadan uygulama kapatılmamalıdır. O halde Çıkış butonumuzda bunun önlemini alalım.

Bir threadin çalışıp çalışmadığını "Alive" özelliğinden algılayabilirsiniz. Bu özellik size true yada false bir karşılık verir.

private void btn_Cikis_Click(object sender, EventArgs e)
{
    if ((!T1.IsAlive) && (!!T2.IsAlive) && (!T3.IsAlive) && (!T4.IsAlive) && (!T5.IsAlive))
    {
        Close();
    }
    else
    {
        MessageBox.Show("#### ÇALIŞAN THREADLER VARKEN UYGULAMA KAPATILAMAZ ####");
    }
}



Uygulama dosyalarını buradan indirebilirsiniz.


Evren AYAN
Microsoft MVP - Connected System Developer
http://www.evrenayan.net