Bu içeriğimizde aşağıdaki hatanın asıl sebebinden bahsedecek, bunun yanında şahsen nerede aldığımı, hangi durumlarda karşımıza çıkabileceğine değineceğim.
Tabi ki de öncelikle hatamızı tanımlamakla işe başlayalım.
Bu hata, birden fazla iş parçacığı(Thread) üzerinde çalışıldığı zamanlarda Thread’ların birbirlerine müdahale etmesi sonucu ve hatadanda anlaşılacağı üzere bir iş parçacığı içerisindeki işlevi farklı bir iş parçacığı içerisinde devreye sokmaktan kaynaklanmakta ve alınmaktadır.
“Bu hatayla sen nasıl bir durumda karşılaştın?” diye sorarsanız eğer
Asenkron bir yaklaşım sergilemeye çalışırken oluşturduğum yapılar birazdan aşağıda göstereceğim gibi farklı iş parçacıklarını birbirlerine harmanlar vaziyetteydi.
Örneğin aşağıdaki gibi bir forma eleman ekleme durumunu ele alırsak eğer;
private void Form1_Load(object sender, EventArgs e)
{
ControlEkle();
}
Task ControlEkle()
{
return Task.Run(() =>
{
Label lblOrnek = new Label();
lblOrnek.Name = "lblOrnek";
lblOrnek.Text = "Örnek Label";
this.Controls.Add(lblOrnek);
});
}
ControlEkle metodu ile forma yüklemeye çalıştığım bir Control nesnesini süreçte asenkron bir yapıda kullanabilmek için Task imzalı bir metodla geriye aynı tipte nesne dördürmeye çalışırken içerisinde yapılan bu işlemler olası ilgili hatayı vermektedirler. Haliyle ControlEkle metodu Form1_Load eventında çağrıldığı anda ana Thread’a müdahale söz konusu olacak ve biryandan da bu ana Thread’da ki öncelikli işlem Task.Run() metodu içerisinde bulunan forma Control ekleme komutları olacaktır. Eee this.Controls.Add() ayrı bir Thread olduğundan dolayı olay karışacak ve program patlayacaktır.
Tabi öncelikle bu işlemlerde bu hatayı alabilmek yani farklı iş parçacıklarını birbirleri denetiminde harmanlamak için öncelikle Control.CheckForIllegalCrossThreadCalls = false; komutunu devreye sokmanız gerekmektedir.
Benzer şekilde aynı işlemi delege kullanarak gerçekleştirirsek eğer;
private void Form1_Load(object sender, EventArgs e)
{
MetodHandler metod = new MetodHandler(ControlEkle);
metod.BeginInvoke(new AsyncCallback((x) => { }), this);
}
delegate void MetodHandler();
void ControlEkle()
{
Label lblOrnek = new Label();
lblOrnek.Name = "lblOrnek";
lblOrnek.Text = "Örnek Label";
this.Controls.Add(lblOrnek);
}
benzer şekilde delege nesnesi içerisinde belirlenen ControlEkle metodu farklı bir Thread’da çalıştırılırken, içerisindeki this.Controls.Add() komutu farklı bir Thread’ı temsil etmekte ve gene olası hatayla karşılaşılmaktadır.
Tabi ki de ilgili örnekleri çoğaltabilir ve örnek olayları genişletebiliriz. Ama farkına vardıysanız genellikle asenkron yapılanmalara yatkın olarak hata seyretmektedir.
Mesele Thread sınıfı aracılığıylada asenkron temelli son bir örnek sunarsak eğer;
private void Form1_Load(object sender, EventArgs e)
{
Thread trd = new Thread(new ThreadStart(ControlEkle));
trd.Start();
}
void ControlEkle()
{
Label lblOrnek = new Label();
lblOrnek.Name = "lblOrnek";
lblOrnek.Text = "Örnek Label";
this.Controls.Add(lblOrnek);
}
senaryosunda da hatayla karşılaşılacaktır.
Gördüğünüz gibi, farklı iş parçacıklarının birbirlerine karıştırıldığı yaklaşımlarda bu olası hata bir aktivasyon göstermekte ve işlevler yerine getirilmemektedir.
Peki bu olası hatayı nasıl çözebiliriz? sorusunun cevabına gelirsek eğer, artık hangi Control elemanını kullanıyorsak o elemana yapılan başvurunun bulunduğu iş parçacığından mı? yoksa farklı iş parçacığından mı? yapıldığını kontrol etmemiz yeterlidir.
BU işlem için aşağıdaki kod bloğunu inceleyeliniz.
private void Form1_Load(object sender, EventArgs e)
{
ControlEkle();
}
Task ControlEkle()
{
return Task.Run(() =>
{
Label lblOrnek = new Label();
lblOrnek.Name = "lblOrnek";
lblOrnek.Text = "Örnek Label";
if (this.InvokeRequired) //Forma gelen talebin farklı bir iş parçacığından gelip gelmediği kontrol ediliyor.
{
//Eğer farklı bir iş parçacığından talep gelmişse aşağıdaki Invoke metoduyla işlem gerçekleştiriliyor.
this.Invoke((MethodInvoker)delegate ()
{
this.Controls.Add(lblOrnek);
});
}
});
}
Gördüğünüz gibi InvokeRequired propertysi ilgili Control elemanına gelen talebin farklı bir iş parçacığından olup olmadığı bilgisini verirken, Invoke metodu aracılığıyla da farklı iş parçasından gelen talep o nesne üzerinde tetiklenmektedir.
Aynı yöntemi diğer örnek olaylar üzerinde de uygularsanız olası hatanın aşıldığını göreceksiniz.
İşte asenkron yaklaşımlı uygulamalarda bu tarz bir hatayla karşılaşmanın en basit ve kesin çözümü bu şekilde gerçekleştirilmektedir.
Okuduğunuz için teşekkür ederim…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…