Aynı anda birden fazla program çalıştırılıp birden fazla işlem yapılırken işlemcinin kaynaklarını uygulamalar arasında paylaştırmayı işletim sistemi yani Windows gerçekleştirmektedir. Windows’un nazarında çalışan her uygulama bir Process’tir ve Process’ler Windows’un Görev Yöneticisi penceresinde listelenmektedir.
C# uygulamanız dahilinde birbirine paralel yapılmasını istediğiniz birden fazla işlem varsa Multithread kavramı ve ilgili sınıflarla ilgilenmeniz gerekir. Başka bir deyişle gerçekte bir process olan C# uygulaması dahilinde birden fazla işlemi birbirine paralel yaptırtma gereğini duyuyorsanız uygulamanızda 2. veya 3. bir kanal yani Thread oluşturmanız gerekir. C# uygulamasındaki ilk Thread’in otomatik olarak hazırlandığını tahmin etmiş olmalısınız.
Bu arada standart özelliklere sahip .NET uyumlu bir uygulama çalıştırıldığı zaman Windows tarafından hazırlanan Process’in içinde bir Application Domain’in, Application Domain içinde ise bir Thread’in hazırlandığını hatırlamak gerekir. Application Domain’ler .NET’e özeldir. Windows, Process ve Application Domain’lerin hiyeraşik yapısını aşağıdaki gibi resmetmek mümkündür.
C# uygulamanız dahilinde uzun süren işlemler söz konusu olduğunda multithread’ler yani çoklu iş kanalları ile ilgilenmeniz gerekir. Örneğin veritabanından okuma yapıp yazdırmak istediğiniz çok sayıda sayfa varsa veya uzak bir bilgisayara Internet üzerinde büyükçe bir dosyayı gönderiyorsanız bu işlemler için ayrı iş kanalları yani Thread’ler hazırlamanız önerilir.
Her C# uygulaması için bazı programcıların Main Thread, bazılarının ise Primary Thread dedikleri bir Thread’in otomatik olarak hazırlandığına dikkatinizi çekmek için Visual Studio ile hazırladığım projenin formuna birkaç TextBox yerleştirip aşağıda verdiğim kodu hazırladım.
private void Form1_Click(object sender, EventArgs e)
{
System.Threading.Thread gecerli_kanal;
gecerli_kanal System.Threading.Thread.CurrentThread;
textBox1.Text = gecerli_kanal.IsAlive.ToString();
textBox2.Text = gecerli_kanal.Priority.ToString();
textBox3.Text = gecerli_kanal.ManagedThreadId.ToString();
}
Bu kodda önce Thread tipinde bir değişken tanımlayıp Thread sınıfının static CurrentThread özelliğinin içeriğini bu değişkene aktardım. Thread sınıfının çok sayıda özelliği olmasına rağmen 3 tanesinin içeriğini okumakla yetindim.
Thread hazırlanırken System.Threading’de bulunan Thread sınıfından yararlanılmaktadır. Bu konuda bilgi vermek için aşağıdaki gibi basit bir hazırladım. Amacım programın uzun süre aynı işle meşgul olmasını sağlamaktır.
private void Form1_Click(object sender, EventArgs e)
{
for (int i = 1; i <= 100; i++)
{
this.Text = i.ToString();
if (i == 50)
{
this.Text ="Projenin çalışması bir süre duracak";
System.Threading.Thread.Sleep(10000);
}
}
}
Döngünün 50. turunda geçerli thread’i Sleep() metodu ile bir süre durdurdum. Burada asıl anlatmak istediğim şudur: Üzerinde çalıştığınız C# projesini çalıştırdığınızda bir Thread otomatik olarak hazırlanmaktadır.
Proje için otomatik olarak hazırlanan Thread’e Temel Thread adı verilmektedir. Temel Thread statik Sleep() metodu ile durdurulmuş iken uygulama donacaktır ve size tepki vermeyecektir. Örneğin bu sırada pencerenin genişliğini değiştirmenize izin verilmez. Döngünün 50. turunda uygulama durmuş iken uygulama penceresini kapatmak isteyince aşağıdaki gibi bir sonuçla karşılaştım. Bu gibi zaman alan işlemler yapılırken kullanıcı uygulamanın kilitlenmiş olduğunu sanabilir ve kapatmak için başka yöntemlere başvurabilir. 
Thread konusu biraz zor anlaşılan bir konu olduğu için izninizle konuyu biraz uzatacağım. Bu amaçla ilk olarak aşağıdaki gibi basit bir metot hazırladım. Çünkü Thread sınıfının yapıcı metodu ThreadStart nesnesini parametre olarak almaktadır. Gerçekte bir delege olan ThreadStart’ın yapıcı metodu ise normal bir metodu parametre olarak aldığı için böyle bir metot hazırladım. Bu metot işletildiğinde döngü değişkenin o anki içeriği formdaki ListBox’a eleman olarak yazılmaktadır. ThreadStart delegesinin yapıcı metoduna parametre olarak verdiğiniz metodun void ve parametresiz olması gerekiyor.
private void islem1()
{
for (int i = 0; i < 10; i++)
{
listBox1.Items.Add(i.ToString() + ". eleman");
}
}
Bu metot işletildiğinde 1’den 10’a kadar olan sayılar eleman olarak formdaki ListBox’a eklenecektır. ThreadStart delegesinin yapıcı metoduna parametre olarak verilecek metodun geriye bilgi gördermemesi yani void olması gerekmektedir. Hazırlanacak Thread’in başlangıç noktası olacak bu metodun içinden başka metotları çağırmak mümkündür.
Thread başlatıldığında işletilecek metodu hazırladıktan sonra sıra bu metodu parametre olarak alacak ThreadStart nesnesini hazırlamaya gelir. Kendi hazırlayacağım Thread’i başlatmak için forma Başlat adında bir düğme yerleştirip bu düğmenin Click olayını temsil eden metodu aşağıdaki gibi düzenleyerek ThreadStart nesnesini hazırladım.
private void Baslat_Click(object sender, EventArgs e)
{
ThreadStart start_1;
start_1 = new ThreadStart(islem1);
}
Bu 2 satır sayesinde ThreadStart nesnesini hazırladıktan sıra Thread nesnesini hazırlamaya gelir. Bu Thread nesnesini başka bir metodun içinden kullanabilmek için değişken tanımlama satırını metodun dışına aldım. Thread nesnesini nasıl hazırladığımı aşağıda görebilirsiniz.
System.Threading.Thread Kanal1;
private void Baslat_Click (object sender, EventArgs e)
{
ThreadStart start_1;
start_1 = new ThreadStart(islem1);
Kanal1 = new System.Threading.Thread(start_1);
}
Bu kod sayesinde “Kanal1” adında bir thread hazırlanmış olur. Bu şekilde thread hazırlandıktan sonra istenildiği zaman Start() metodu ile başlatmak mümkündür. Başlattğınız Thread’i istediğiniz zaman durdurabilirsiniz.
private void Baslat_Click(object sender, EventArgs e)
{
ThreadStart start_1;
start_1 = new ThreadStart(islem1);
Kanal1 = new System.Threading.Thread(start_1);
Kanal1.Start();
}
Çalışma anında bu kod işletilip Start() metodu ile thread devreye sokulursa hata(cross-thread hatası) meydana gelir ve aşağıdaki gibi bir hata mesajı alınır. Bu hatayı; kendi anahtarınla gidip başkasına ait arabayı sürmeye çalışmaya benzetebiliriz. Başka bir anlatımla; ListBox’a eleman eklemek istemekle C# projesinin otomatik olarak hazırladığı Thread’in kontrolündeki formun üzerindeki nesneyi kendi hazırladığım Thread dahilinde kullanmak istemiş oldum. Hemen ekleyelim; bu kod Visual Studio 2003 yani C# 1.0 ile hazırlanmış olsaydı bu hata meydana gelmezdi. Çünkü C# 1.0 yani .NET Framework 1.1’de bu işleme izin veriliyordu.

Yukarıda hazırlayıp ThreadStart delegeesinin yapıcı metodunda, form ve forma yerleştirilen nesneler kullanılmamış olsaydı Start() metodu ile başlatılan Thread “islem1” adını vermiş olduğum metodun sonuna geldiğinde kendiliğinden kapanırdı. Henüz çalışması sona ermemiş Thread tekrar çalıştırılmak istenirse ThreadStartedException hatası ile karşılaşılır.
Windows Forms Uygulamalarında Thread’ler
Thread’ler hakkında bilgi verilirken örneklerde konsol uygulamasını tercih etmek anlatan için bir avantajdır. Bu nedenledir ki Thread’lerin kullanımını anlatmak üzere hazırlanan örneklerin neredeyse tamamı konsol uygulamasıdır. Windows Forms uygulamalarında çoklu thread kullanımı hakkında bilgi vermek için forma bir ListBox ve bir düğme yerleştirip aşağıda verdiğim kodu yazdım.
delegate void delege1();
private void islem1()
{
if (this.listBox1.InvokeRequired)
{
delege1 Yeni_delege = new delege1(islem1);
this.Invoke(Yeni_delege, new object[] { });
}
else
{
for (int i = 0; i < 10; i++)
{
this.listBox1.Items.Add((i+1).ToString() + ". eleman");
this.listBox1.Refresh();
Thread.Sleep(500);
}
}
}
Öncelikle bu metodun dışında delegate tipinde bir değişken tanımladım. “islem1” adını verdiğim metodun içinde ise forma yerleştirdiğim ListBox’ın farklı bir Thread tarafından kontrol edilip edilmediği araştırılıyor. Bu kontrol ListBox’ın InvokeRequired özelliğine bakılarak yapılıyor. Bu özellik burada true olacağı için delegate nesnesi ile islem1() adını verdiğim metot yeni baştan çalıştırılıyor.
Bu şartlarda ListBox’a binlerce eleman eklense bile işlem kısa sürer. Süreyi uzatmak için her döngü turunda Sleep() metodu ile işlemi bir süre durdurdum. Bu şekilde ayrı bir Thread olarak değerlendirmek istediğim metodu hazırladıktan sonra formdaki düğmenin Click olayını temsil etmek üzere aşağıda verdiğim metodu hazırladım.
System.Threading.Thread Kanal1;
delegate void delege1();
private void Baslat_Click(object sender, EventArgs e)
{
ThreadStart start_1;
start_1 = new ThreadStart(islem1);
Kanal1 = new System.Threading.Thread(start_1);
Kanal1.Start();
}
Bu şartlarda uygulamamız sorunsuz çalışır. Tabii bu çalışmanın gerçekleşebilmesi için C# derleyicisi geri planda bir çok işlem yapar. Demek istediğim şudur: .NET Framework uyumlu pencereli her uygulamada her Thread’ın kendi mesaj kuyruğu olmaktadır. Visual Studio ile hazırlanan Windows Forms uygulamalarında mesaj kuyruğu Application sınıfının Run() metodu ile hazırlanmaktadır. Yani yukarıda 2. bir Thread hazırladık ama Current yani geçerli Thread’a ait formdaki ListBox’a bilgi aktarmaya çalıştık. Güvenlik engeli ile karşılaşınca aracılık yapan bir delegate’ten yararlandık. Aşağıda verilen ekran görüntüsünü kendi hazırladığım Thread çalışmaya devam ederken aldım.

Konunun ayrıntılarını ortaya çıkarabilmek için bu forma 2. bir ListBox ve 2. bir düğme yerleştirdim. Başka bir deyişle bu Windows Forms uygulaması dahilinde CurrentThread dışında 2 Thread hazırlayacağım. Bu Thread’lerin birisi ilk ListBox’a eleman eklerken diğer Thread 2. ListBox’a eleman ekleyecek. Formdaki 2. ListBox’ı kullanacak Thread için hazırladığım metodu aşağıda görebilirsiniz.
delegate void delege2();
private void islem2()
{
if (this.listBox2.InvokeRequired)
{
delege2 Yeni_delege_2 = new delege2(islem2);
this.Invoke(Yeni_delege_2, new object[] { });
}
else
{
for (int i = 0; i < 10; i++)
{
this.listBox2.Items.Add((i+1).ToString() + ". eleman");
this.listBox2.Refresh();
Thread.Sleep(500);
}
}
}
Bu hazırlıktan sonra forma 2. bir düğme yerleştirip aşağıda verdiğim kodu yazdım. Buna göre kullanıcı formdaki ikinci düğmeyi tıkladığında 2. bir Thread hazırlanır ve islem2() adını verdiğim metot sayesinde 2. ListBox’a bilgi yazılır.
private void Baslat_2_Click(object sender, EventArgs e)
{
ThreadStart start_2;
start_2 = new ThreadStart(islem2);
Kanal2 = new System.Threading.Thread(start_2);
Kanal2.Start();
}
Tabi bu şartlarda önce formdaki ilk düğme tıklanıp “Kanal1” adını verdiğim thread devam ederken 2. düğme tıklanıp “Kanal2” adlı thread çalıştırılmak istenirse 1. Thread’in çalışması sona ermeden sıra ikincisine gelmez.
Normalde projede kaç form olursa olsun bütün formlar ve formlara yerleştirilen bütün nesnelerle ilgili mesajlar Application sınıfının Run() metodu tarafından yakalanıp ilgili form ve nesneye gönderilmektedir.
Yukarıda “Kanal1” ve “Kanal2” adını verdiğim Thread’lerin mesaj kuyruklarındaki mesajlar işlenmez. Çünkü bu Thread’lere ait mesaj kuyruklarındaki mesajları alıp gereklerini yerine getirmek için herhangi bir işlem yapmadık. Bu konuda bilgi vermek için 3 forma sahip bir proje hazırladım. Projenin 2. ve 3. formlarını ayrı birer Thread olarak değerlendirip ekrana getireceğim. Bu amaçla ilk forma 2 düğme yerleştirdim ve bu forma ait kod dosyasında aşağıdaki gibi 2 ayrı metot hazırladım.
private void islem1()
{
Form2 F2 = new Form2();
F2.Show();
}
private void islem2()
{
Form3 F3 = new Form3();
F3.Show();
}
Projenin ilk formunu temsil eden “Form1.cs” dosyasında bu şekilde 2 metot hazırladıktan sonra ilk forma yerleştirdiğim button nesnelerinin Click metotlarını temsil etmek üzere aşağıdaki gibi 2 metot hazırladım.
System.Threading.Thread Kanal1;
private void button1_Click(object sender, EventArgs e)
{
ThreadStart start_1;
start_1 = new ThreadStart(islem1);
Kanal1 = new System.Threading.Thread(start_1);
Kanal1.Start();
}
System.Threading.Thread Kanal2;
private void button2_Click(object sender, EventArgs e)
{
ThreadStart start_2;
start_2 = new ThreadStart(islem2);
Kanal2 = new System.Threading.Thread(start_2);
Kanal2.Start();
}
Buna göre çalışma anında projenin başlangıç formundaki ilk düğme tıklandığında “Kanal1” adında bir Thread hazırlanır. Bu Thread’in başlangıç noktası islem1() adını verdiğim metottur. Benzer şekilde başlangıç formundaki 2. düğme tıklandığında ise Kanal2 adında başka bir Thread hazırlanır. Kanal2 adlı Thread’in başlangıç noktası ise islem2() adını verdiğim metottur. islem1() adlı metotta projedeki 2. formun örneği alınıp Show() metodu ile ekrana getirilmektedir. Benzer hazırlık “islem2()” metodunda yapılmaktadır.

Bu hazırlıktan sonra proje çalıştırılıp düğmelerden birisi tıklandığında ilgili formun ekrana geleceğini sanabilirsiniz. Herhangi hatalı bir durum olmasa bile islem1() ve islem2() metotları ayrı birer Thread oldukları için, başka bir deyişle pencereli bu Thread’lerle ilgili mesajlar yakalanmadığı için formlar ekrana gelmezler. Bu nedenle Kanal1 ve Kanal2 adlı Thread’lerin başlangıç metotlarını aşağıdaki gibi düzenlenip Application sınıfının Run() metodu ile mesajlar yakalanmalıdır.
private voidislem1()
{
Form2 F2 = newForm2();
Application.Run(F2);
}
private voidislem2()
{
Form3 F3 = newForm3();
Application.Run(F3);
}
* Bu metinde yer verdiğim 3-5 satırlık kodlarda tanımladığım değişkenlere ad seçerken yerli programcılık kitaplarından değişken adı çalmadığımı beyan eder ve değişken adlarındaki muhtemel benzerlikler tesadüften ibarettir. Aslında Kanal1 ve Kanal2 yerine kanel1, kanel2, alleben_deresi1 ve alleben_deresi2 adında değişkenler tanımlamış olsaydım değişken adı benzerliğinden dolayı suçlanıp sanık pozisyonunda tekrar hakim karşısına çıkma ihtimali belki biraz azalırdı.
Visual C# İçin ADO.NET Kitabı:Rakibi Çok Yerini Tutan Yok
Önce şu ADO.NET nedir sorusuna cevap verelim: Visual Basic 3.0’te veritabanı işlemleri yapılırken kullanılan teknolojinin adı DAO yani Database Access Object’i. DAO, Microsoft tarafından geliştirilen JET adlı veritabanı motorunu ve Windows’la gelen ODBC sürücülerini kullanıyordu. Microsoft firması bir süre sonra Visual Basic programcıları ADO’yu yani ActiveX Data Objects’i geliştirdi. 2002 yılında piyasaya verilen ilk .NET Framework sürümüyle birlikte ise ADO.NET programcıların istifadesine sunuldu. .NET Framework’le gelip veritabanı işlemleri yapılırken yararlanılan sınıfların hepsine birden ADO.NET adı verilmektedir.

Bu kitabın hedef okur grubu daha önce C# hakkında az çok bilgi edinmiş olmasına rağmen C# uygulamalarında veritabanı işlemlerini yapmakta zorlananlardır. Bu hedef okur grubu içinde daha önce SQL Server’la hiç ilgilenmemişlerin olabileceğini düşünerek SQL Server hakkında özet bilgi vermeyi denedik. Yani daha önce SQL Server’la hiç çalışmamış hiç uğraşmamış olanlar bile bu kitabı okuduklarında rahatlıkla SQL Server’ı kurup veritabanı hazırlayıp C# uygulaması dahilinde istedikleri veritabanı işlemlerini yapabilir duruma gelirler. Bu kitabı okuyanlar SQL veya Access veritabanlarına bağlanırken sorun yaşamazlar, uygulamalarını dağıtmak için akla karayı seçmezler, yazılanları anlamak için tekrar tekrar okumak zorunda kalmazlar.
SQL Server Veritabanları Access Veritabanları SqlConnection ve OleDbConnection Sınıfları BindingSource ve BindingNavigator Command Nesneleri DataGrid ve DataGridView DataReader ve DataAdapter Sınıfları DataSet ve DataTable Stored Procedure ve Trigger Hazırlamak Blob Alanlar Xml Dosyaları Transaction’lar CLR Destekli Veritabanı İşlemleri Uygulamaları Dağıtmak ClickOnce Teknolojisi Crystal Reports
Bu kitabı aşağıda verilen adresten temin edebilirsiniz.
http://www.hepsiburada.com/visual-c-icin-ado.net-2.-cilt/productDetails.aspx?categoryid=211651&productid=kseckin10852
|