Windows Forms veya WPF (windows presentation foundation) uygulamalarında, multithreaded işlemler yapmaya çalıştığımızda bazen şu hatayla karşılaşırız: “Cross-thread operation not valid”.
Örneğin bir windows forms uygulamasında, işlem başlat diye bir buttonumuz olduğunu düşünelim. Buttona basılınca arka planda bir işlem başlatalım. İşlemin uzun süreceğini varsayalım. İşlem süresince, form üzerinde işlem yapamayacağım. Aynı ui thread’ı kullanacağımız için Formu resize, minimize veya yerini değiştirmek gibi işlemleri yapamayacağız. Tüm eventler ancak işlem bittiğinde gerçekleşecek. Bu durumdan kurtulmak için şöyle bir kod yazdığımızı düşünelim.
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace uithreadtest { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private async void button1_Click(object sender, EventArgs e) { Task task = Task.Run(() => routine()); await task; } private void routine() { Thread.Sleep(5000); MessageBox.Show("test"); } } }
.NET Framework 4.5 ile birlikte gelen async/await özelliklerinin örnek bir kullanımını görebiliriz.
Buttona tıklayınca, 5 saniye sürecek olan Thread.Sleep metodunu çağırıyoruz. Bu süre zarfında, async/await özelliklerini kullandığımız için ekranımız tepkilere cevap verecektir. Herhangi bir donma ile karşılaşmadan ekranda istediğimiz şeyleri yapabileceğiz.
Şimdide routine metodunda, MessageBox.Show kullanmak yerine formda bulunan richTextBox1 componentinin text alanını set edelim.
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace uithreadtest { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private async void button1_Click(object sender, EventArgs e) { Task task = Task.Run(() => routine()); await task; } private void routine() { Thread.Sleep(2000); richTextBox1.Text = "süre doldu"; } } }
Programı çalıştırıp işlemi başlattığımızda, şu hatayı alacağız: Cross-thread operation not valid: Control ‘richTextBox1’ accessed from a thread other than the thread it was created on.
Bu durum Windows Forms veya WPF uygulamalarında threading mantığını doğru kullanmadığımızdan kaynaklanmaktadır. İnternette bu durumla ilgili bazı kaynaklar var. MSDN’de problemi detaylı bir şeklinde açıklayan bir bölüm var: https://msdn.microsoft.com/en-us/library/ms171728(v=vs.110).aspx
Sorunu aşmak için InvokeRequired ve Invoke metodlarını kullanabiliriz:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace uithreadtest { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private async void button1_Click(object sender, EventArgs e) { Task task = Task.Run(() => routine()); await task; } private void routine() { Thread.Sleep(5000); if (richTextBox1.InvokeRequired) { richTextBox1.Invoke(new MethodInvoker(delegate { richTextBox1.Text += "süre doldu" + Environment.NewLine; })); } else { richTextBox1.Text += "süre doldu" + Environment.NewLine; } } } }
Tekrar işlemi başlatalım: