Cross Threading - II

Cross Threading - II Cross Threading - II     Windows Form kontrollerine erişimler thread güvenli(thread-safe) değildir. Eğer iki ya da daha fazla thread ile bir kontrolün durumunu değiştiriyorsanız, kontrolü tutarsız bir hale gelmesi muhtemeldir. Race condition ve deadlock gibi benzer thread problemlerinin oluşması ihtimalini de arttırmış oluyoruz. Ayrıca bir önceki makalede bahsettiğimiz güvenlik problemlerinin de oluşmasını engellemek için erişimleri thread güvenli (thread-safe) bir hale getirmek önemli olacaktır.Microsoft .NET Framework 2.0, kontrollere thread güvenli(thread-safe) olmayan bir şekilde erişilmesi durumunda sizi uyarmaktadır. Şimdilik bu uyarı debugging zamanında uygulama geliştiriciye iletilmektedir. Uygulamanız çalışırken hiç bir hata almamanıza karşın debug yaparken "Control control name accessed from a thread other than the thread it was created on" şeklinde bir mesaj ile InvalidOperationException atılır.Bu durumu örnek bir uygulamayla oluşturup nasıl düzeltildiğini inceleyeceğiz. Bu durumu basit anlamda oluşturabilmek için uygulamamız yüklenirken önyükleme (splash) ekranı göstereceğiz ve geçen makalemizdeki gibi yükleme işleminin yavaş sürmesi için bekletme yapacağız.Visual Studio .NET 2005 ile yeni bir windows uygulaması oluşturalım. Uygulamımıza yeni bir form ekleyelim. Bu formun üzerine bir picture box ekleyip Image olarak uygulamamızın önyükleme resmini seçelim. Picture Box’ın docking özelliğini filled olarak ayarlayarak resmin tüm formu kaplamasını sağlayalım. Form’un control boxkullanımını kapatıp, text’ini boş string vererek tam bir önyükleme formu oluşturabiliriz. Burada yapacaklarınız sizin tasarım zevkinize kalmıştır.Uygulamamızdaki tüm pencere ve dialog kutucuklarını kontrol edebilmek için “DialogManager”isimli bir sınıf oluşturalım. Bu sınıf için örnek bir kod aşağıda verilmiştir:using System; using System.Collections.Generic; using System.Text; namespace CrossThreading { public sealed class DialogManager { #region Singleton Implementation #region Private Members private static DialogManager current; private static object synObject = new object(); #endregion #region Property Declarations public static DialogManager Current { get { if (current == null) { lock (synObject) { // Double Checked Null Design Pattern if (current == null) current = new DialogManager(); } } return current; } } #endregion #region Internal Constructor internal DialogManager() { } #endregion #endregion #region Instance Data private SplashForm mySplashForm = null; #endregion #region Public Methods public void ShowSplashScreen() { mySplashForm = new SplashForm(); System.Threading.Thread thDoSplash = new System.Threading.Thread( new System.Threading.ThreadStart(DoSplash) ); thDoSplash.IsBackground = true; thDoSplash.Start(); } public void CloseSplashScreen() { mySplashForm.Close(); } #endregion #region Private Helpers private void DoSplash() { mySplashForm.ShowDialog(); } #endregion } }Uygulamımızın ana formunun kurucu metodunu ve Load eventlerini aşağıdaki gibi değiştirelim:using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace CrossThreading { public partial class Form1 : Form { public Form1() { DialogManager.Current.ShowSplashScreen(); InitializeComponent(); DoSomething(); } private void Form1_Load(object sender, EventArgs e) { DialogManager.Current.CloseSplashScreen(); Activate(); } private void DoSomething() { System.Threading.Thread.Sleep(5000); } } }Bu kodu çalıştırdığınızda, her şeyin tam istediğimiz gibi olduğunu görebilirsiniz.Uygulamamız 5 saniye içinde yükleniyor ve bu arada önyükleme ekranımız sorunsuz olarak gösterilmektedir. Uygulamanız hazır olduğunda yani Load event"inde önyükleme ekranı otomatik olarak kapanacaktır. Fakat uygulamamızı debug yaptığımızda izin verilmeyen thread çağrımları yapıldığından şikayet eden “Invalid Operation Exception”atıldığını göreceksiniz. Şu anki windows versiyonları çalışma zamanında bu hatayı vermemektedir ancak yeni windows versiyonu olan Windows Vista (kod adıyla Longhorn)çalışma zamanında bu hatayı geçen makalemizde bahsettiğimiz sebeplerden ötürü verecektir.Bu durumda iki seçeneğiniz var, ya .NET Framework"ten cross threading kontrolleri atlamasını isteyeceksiniz ya da bu problemi düzelteceksiniz. Bu kontrolllerin atlanması için "SplashForm.cs" kodunda formun "CheckForIllegalCrossThreadCalls"özelliğini "false" olarak işaretleyebilirsiniz. Fakat tabi ki de bu şekildebir geçici çözüm uydurmak doğru olmayacaktır.Kalıcı ve gerçekçi bir çözüm için “SplashForm.cs” dosyasına aşağıdaki gibi yeni bir public metot ekleyelim.public void ShowModal(ref bool running) { this.Show(); while (running) { Update(); Application.DoEvents(); } this.Close(); }İkinci olarak da “DialogManager.cs” dosyasındaki ilgili metotların artık bu yeni metotu kullanmasını sağlayalım:using System; using System.Collections.Generic; using System.Text; namespace CrossThreading { public sealed class DialogManager { #region Singleton Implementation #region Private Members private static DialogManager current; private static object synObject = new object(); #endregion #region Property Declarations public static DialogManager Current { get { if (current == null) { lock (synObject) { // Double Checked Null Design Pattern if (current == null) current = new DialogManager(); } } return current; } } #endregion #region Internal Constructor internal DialogManager() { } #endregion #endregion #region Instance Data private bool isShowingSplashScreen = false; private SplashForm mySplashForm = null; #endregion #region Public Methods public void ShowSplashScreen() { mySplashForm = new SplashForm(); System.Threading.Thread thDoSplash = new System.Threading.Thread( new System.Threading.ThreadStart(DoSplash) ); isShowingSplashScreen = true; thDoSplash.IsBackground = true; thDoSplash.Start(); } public void CloseSplashScreen() { isShowingSplashScreen = false; } #endregion #region Private Helpers private void DoSplash() { mySplashForm.ShowModal(ref isShowingSplashScreen); } #endregion } } Yukarıdaki kodda verildiği gibi threadler arasında ortak bellek üzerinde bir referans geçirerek (iki thread"ın ortak bellek kullanımı ancak aynı process içindeyken mümkündür),ve bu referansın değerini değiştirerek bu işlemi yapmış olduk. Benzer bir çözüm delegate ve asenkron metot kullanımıyla da yapılabilirdi. Ancak sizin de farkettiğiniz gibi her duruma göre ayrı bir çözüm bulunması gerekir ve bu da bulabileceğimiz en basit çözüm yoludur. Bu çözüm yolundan esinlenerek farklı senaryolarda benzer çözümler üretebilirsiniz. Bu kodlamayı da içeren yüksek lisans tezimdeki uygulamanın önyükleme ön yüzünün ekran görüntüsüyle sözlerimi tamamlamak istiyorum. Ayrıca dikkatinizi çekmek istediğim bir diğer nokta da uygulamanıza böyle bir önyükleme ekranı koymak, uygulamanızı yavaşlatacaktır. Fakat daha kullanıcı dostu bir arayüze sahip olacaksınız.Bir dahaki makalemizde görüşünceye kadar güvende kalın..

Döküman Arama

Başlık :

Kapat