Bu makalemizde oluşturduğumuz windows uygulamasında aynı anda birçok işlemi yapacağız. İlk önce Thread'ın uygulamalarımızda ne gibi faydaları olacağından bahsedelim.Mesela oluşturduğunuz programda çok uzakta bulunan bir sunucudan dosya indiriyorsunuz ve aynı anda indirilme işlemi uzun sürdüğünden başka işlem olarakta diger sunucunuzda bulunan veritabanına veri girmek istiyorsunuz.İşte burada Thread kullanılmaz ise birinci işlem olan dosya indirme işlemi bitmeden kesinlikle yeni kayıt giremezsiniz çünkü programınızın çalıştığı Main Thread'i o anda meşgul olduğundan veri girişi başarısız olacaktır.Ama biz iş mantığımız doğrultusunda Thread oluşturur isek bu işlemleri çok kolay yapacağız.Yani bir dosyayı indirirken bir yandan da diğer veritabanımıza veri girebileceğiz.Çok güzel birşey :).
Aşağıda görsellik açısından anlamasıda kolay olacağından ProgressBar kontrolleri üzrerinden örnek vereceğim.3 adet progressBar var ve 3 tanede çalışan metodum var.Programımızda bu metotlar belirtildiği deger kadar for döngüsü dönerek değerin tamamlanma işlemini progressBar'da görsel olarak gösterecek.Burada Thread kullandığımız için Hepsini Başlat butonuna tıklandığında 3 metotda çalışacak çünkü 3'üde farklı Thread'larda olacaktır.Thread kullanmamış olsaydık önce birinci metot çalışacak progressBar tamamlanaca.Sonra başlayacak ve bu dolduğunda 3'üncü metot çalışacak.İşte bunu engellemek için Thread kullanacağız ve 3 metoduda oluşturduğumuz 3 Thread üzerinden çalıştırarak başarılı bir şekilde işlemimizi tamamlayacağız.
Programımızın görünümü aşağıdaki gibidir.Kodlamaya başlamadan önce NameSpace'lere using System.Threading;'e eklemeyi unutmayın !

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace MultiThread
{
public partial class Form1 : Form
{
//3 farklı işlem yapacağımızdan 3 Thread nesnesi tanımlıyoruz.İŞlemci üzerinde açılacak kanallar diyebiliriz.System.Threading; namespace'ini eklemeliyiz nesneyi tanımlamak için.
Thread IslemKanal1;
Thread IslemKanal2;
Thread IslemKanal3;
public Form1()
{
InitializeComponent();
//Default olarak olusturduğumuz Threadler arasında erişim yasaklanmıştır.Main Thread'den formumuzdaki bir kontrole erişmek istersek exception alırız.Bu hatayı almayıp Threadler arası iletişimi sağlamak için 2 farklı yol kullanabiliriz.1.yolumuz eğer bizim Thread'ımızda formdaki birçok kontrole erişim varsa CheckForIllegalCrossThreadCalls = false; dememiz lazım.CheckForIllegalCrossThreadCalls bir propertytidir.False değeri formdaki tüm kontrollere threadlerden ulaşılabilir anlamına gelir.
//2. yolumuz ise biraz daha güvenli olanı diyebiliriz.Tüm kontrollere erişim sağlanması yerine sadece istediğimiz kontrole erişim hakkı verebiliriz.ProgressBar classının CheckForIllegalCrossThreadCalls propertisine false değeri verirsek programımızdaki tüm proggressBar'ların threadlar arasında işlemlere açık olmasını sağlamış oluruz.
ProgressBar.CheckForIllegalCrossThreadCalls = false;
//Uygulamamızın bulundugu Main thread içerisinde işlemlerimiz için farklı kanallar oluşturup işlemleri bu threadler üzerinden yapacağız.Buradaki programımızda 3 farklı kanala ihtiyaç olduğundan kanallar içerisinden yürütülecek olan işlemleri(yazdıgımız metotlar) çağırımları runtime yani çalışma zamanında kullanıcının belirleyeceğinden delegate nesnesine ihtiyacımız olacaktır.Multithread uygulamalarda metotlar doğrudan çağıralamaz , oluşturduğumuz Thread nesneleri üzerinden runtime esnasında çalıştırılır , duraklatılır , durdurulur ve işlem sonuçları yine runtime'da forma aktarılır.Bu yüzden Thread nesnesi metodu işaretleyecek bir delegate nesnesine ihtiyaç duyacaktır.Delegate görünümü aşağıdaki gibidir.Dikkat ederseniz çantaya benzeyen bir icon ve içerisinde mor bir şekil var.Bu icondan ve yanındaki açıklamayada bakarsak bu icona sahip herşeyin delegate oldugunu anlarız
"/pics/MakaleResim/delegate.gif" alt="" width="650" height="165" />
//Buradaki Islem1,Islem2 ve Islem3 programda çalışacak olan metodumuz.Demiştik ya delegate ile işaretlenmesi lazım diye işte bu yüzden ThreadStart isimli delegate ile metodu işaretleyip is1,is2 ve is3 adında delegate nesnesi oluşturuyoruz.
ThreadStart is1 = new ThreadStart(Islem1);
ThreadStart is2 = new ThreadStart(Islem2);
ThreadStart is3 = new ThreadStart(Islem3);
//Tüm işlemlerimizi kanallar üzerinde yapmak için en üstte oluşturduğumu Thread nesnelerini new anahtar kelimesi ile örnekleyip o Thread nesnelerini örnekliyoruz.
IslemKanal1 = new Thread(is1);
IslemKanal2 = new Thread(is2);
IslemKanal3 = new Thread(is3);
}
//Buradaki Islem1() , Islem2() , Islem3() metotları bizim programımızdaki yapmak istediğiğim işlemlerin bulunduğu metotlardır.Burada en basitinden bir for döngüsü ile 3 tane progressBar'ımıza for dönüsü değerince doldurmasını sağlıyoruz.Bu işlemlere windows'u örnek verebiliriz aynı anda müzik dinleyip internette gezip program çalıştırabiliyoruz.Olayın mantığı bu.Burdaki örneğimize bakarsak da önce progressBar1 dolar sonra progressBar2 ve buda dolduktan sonra en son progressBar3 dolar.Ama thread kullanırsak aynı anda 3 progressBar'ada müdahale edip hepsini aynı anda başlatıp aynı anda duraklatıp aynı anda da iptal edebiliriz.Daha sonra metotlarımızı yazıyoruz.
void Islem1()
{
for (int i = 0; i < 50000; i++)
{
progressBar1.Value = i / 500;
label4.Text = i.ToString();
}
}
void Islem2()
{
for (int i = 0; i < 50000; i++)
{
progressBar2.Value = i / 500;
label5.Text = i.ToString();
}
}
void Islem3()
{
for (int i = 0; i < 50000; i++)
{
progressBar3.Value = i / 500;
label6.Text = i.ToString();
}
}
//Tüm işlemlerimizi başlatan buton.Clicklendiğinde kanallarımızda işaretlemiş oldugumuz metotlar çağıralarak aynı anda tüm progressBarlarımızda işlem yürütülmüş olup hemen progressBarın yanında da o anki değeri yazar.
private void btnHepsiniBaslat_Click(object sender, EventArgs e)
{
//İşlemleri başlatmak için yukarıda tanımlamış olduğumuz IslemKanal isimli Thread'ların Start() isimli metodunu kullanarak işlemlerde bulunan metotların çalışmasını sağlamış oluyoruz.
IslemKanal1.Start();
IslemKanal2.Start();
IslemKanal3.Start();
}
//O anda thread'ımızda çalışan tüm işlemleri durdurmak için çalışan click olayı
private void btnHepsiniDurdur_Click(object sender, EventArgs e)
{
//oluşturduğumuz thread'ların Suspend() metodu o anda çalışan thread'ı durdurur.
IslemKanal1.Suspend();
IslemKanal2.Suspend();
IslemKanal3.Suspend();
}
private void btnHepsiniDevamEttir_Click(object sender, EventArgs e)
{
//Thread'larımızda durdurdugumuz işlemleri burada Thread adını belirterek o duran işlemi devam ettiriyoruz.Hepsi devam ederken yine devam ettire veya hepsi durmuş iken tekrar hepsini durdura basarkan anlaşılacağı gibi mantık hatası oluşur ve uygulama exception fırlatır.Resume metodu bu işe yarar.
IslemKanal1.Resume();
IslemKanal2.Resume();
IslemKanal3.Resume();
}
private void btn2SaniyeDuraklat_Click(object sender, EventArgs e)
{
//Tüm çalışan thread'leri Sleep() metoduyla duraklatabiliriz.Milisaniye cinsinden deger alır ve 2 saniye duraklatmak için 2000 değerini verdik.Duraklattıktan 2 saniye geçtikten sonra tüm işlemlerimi tekrar kaldığı yerden devam edecektir.
Thread.Sleep(2000);
}
private void btn2IslemiDuraklat_Click(object sender, EventArgs e)
{
//İkinci işlemimizi duraklatıyoruz
IslemKanal2.Suspend();
}
private void btn2IslemiDevamEttir_Click(object sender, EventArgs e)
{
//İkinci işlemi kaldı yerden devam ettiriyoruz.
IslemKanal2.Resume();
}
//Formclossing eventinde eğer formumuzda işlemler devam ediyorsa form kapatılırken uyarı vermesini sağlayacagız.
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
//ISAlive özelliği baglı olduğu thread eğer sonlanmışmı veya durmuşmu onu kontrol edebiliriz.Bu özellik burada true değer döndürecektir.
if (IslemKanal1.IsAlive || IslemKanal2.IsAlive || IslemKanal3.IsAlive)
{
//işlemler çalışırken form kapatılırsa mesaj veriyoruz.
DialogResult sonuc = MessageBox.Show("Çalışan işlemler bulunduğunda programı kapatamazsınız","Uyarı ! " , MessageBoxButtons.YesNo);
//Çıkan mesaj kutusunda kullanıcı YES butonuna tıklarsa uygulamamızı kapatıyoruz.
if (sonuc == DialogResult.Yes)
Environment.Exit(1);
else
//Yes demez ise kapatmayı iptal ediyoruz.
e.Cancel = true;
}
}
}
}
Resimde de görüldüğü gibi programımız sağlıklı bir biçimde çalışmaktadır.Windows'un Görev Yöneticisinde bulunan İşlemler sekmesi altında o anda çalışan 10 larca işlemi görebilirsiniz.Burda herhangi birinin üzerinde tıkladığınızda öncelik sırası atayabiliyorsunuz.Bunu uygulamalarınızda da yapabilirsiniz. Örneğimiz üzerinden konuşacak olursak oluşturduğumuz IslemKanal1 Thead'ını mesela bir butona tıkladığımızda diğer çalışan 2 kanalın önüne gietirmesini sağlayabiliriz.Thread'ımızın Priority propertsi ThreadPriority tipinden enum değeri alır ve en öne geçmesini sağlar.
IslemKanal1.Priority = ThreadPriority.Highest;
IslemKanal2 Thread'ında çalışan işlemide en yavaş yani öncelik sırası en düşük belirleyip işlemci tarafında önemsiz yapmak istiyorsak Highest değilde Lowest değeri vermemiz gerekecektir.
IslemKanal2.Priority = ThreadPriority.Lowest;
Örnek projeyi indir
Makalenin faydalı olması dileğiyle kendinize iyi bakın :)
Serhat TAŞ