میدانید که در پروژه و نرم افزارهایی که توسعه داده میشوند بعضی مواقع مشاهده میکنیم قسمتی از برنامه نیاز به زمان یا پردازش بیشتری دارد تا عملیات خود را به اتمام برساند و رابط کاربری (User Interface) برنامه در این حین منتظر میماند و یا به اصطلاح (Freeze) میشود تا یک پردازش طولانی به اتمام برسد و بعد رابط کاربری به کار خود ادامه میدهد و بعضی مواقع پنجره Windows explorer is not responding را مشاهده کردهاید که با کلیک بر روی Close the program از آن گذر میکنیم. در حالی که برنامه ما باید حالت Responsive داشته باشد و نباید برنامه با یکچنین مواردی روبه رو شود.
ساختمان Thread :
تمامی اشیاء (Objects) در این مدل به دو گروه تقسیم بندی میشوند:
STA شامل فقط یک Thread میباشد و تمامی اشیاء در این ساختمان فقط میتوانند متدی را که در این Thread صدا زده میشود دریافت کنند؛ یا به عبارتی دیگر هنگامیکه شیء به Thread ای متصل میشود دیگر شما قادر نخواهید بود اشیاء UI را به صورت مستقیم و یا از طریق Threadهای دیگر تغییر دهید. لازم به ذکر است WPF از مدل STA پشتیبانی میکند که شامل نکات زیر میباشد:
1. یک Thread در کل برنامه اجرا میشود و شامل همه Objectهای WPF میباشد.
2. عناصر یا المانهای WPF، قابلیت تنظیم Thread Affinity را دارند. منظور این میباشدکه Threadها نمیتوانند با یکدیگر ارتباط برقرار کنند.
3. اشیاء WPF که قابلیت Thread Affinityرا دارند، از یک Object Dispatcher مشتق میشوند.
4. اشیاء WPF متعلق به Thread ای میباشند که توسط آن ایجاد شده است و Thread دیگر قادر به دسترسی مستقیم به این اشیاء نمیباشد.
ساختمان Multi-Threaded Apartment (MTA) :
MTA شامل یک یا چندین Thread میباشد. تمامی اشیاء در این مدل میتوانند از هر Thread ای فراخوانی شوند. در حقیقت رجیسترهای CPU و Ram که در اختیار برنامه قرار داده شده تکه تکه میشوند و برنامه ما تعداد Threadهای مورد نظر را اجرا میکند و یکی از راه حلهای بر طرف کردن اینکه رابط کاربری ما در حالت انتظار باقی نماند استفاده از پردازش کارها بصورت غیر همزمان (Asynchronous) میباشد که در اصطلاح Multithreading نامیده میشود.
WPF Dispatcher :
WPF از مدل STA پشتیبانی میکند. زمانیکه برنامه WPF اجرا میشود، به طور خودکار یک Dispatcher Object ساخته میشود و متد Run صدا زده میشود و از آن برای آماده سازی صف پیامها استفاده میشود که مدیریت یک صف از کارها بر عهده آن است و کارهای UI را در یک صف FIFO اجرا میکند. WPF یک Dispatcher را برای UI Thread ایجاد میکند. بنابراین شما نمیتوانید یک dispatcher دیگر برای آن تعریف کنید.
نکته : دیاگرام زیر نمایش میهد که تمامی اشیاء WPF
از DispatcherObject مشتق شدهاند.
نکته : زمانیکه برنامه WPF اجرا میشود دو Thread ساخته میشود:
1. UI Thread (Main Thread)
2. Render Thread
UI Thread : مسئولیت تمامی ورودیهای کاربر، handle events, paints screen و اجرای کدهای برنامه را بر عهده دارد.
Render Thread : در Background اجرا میشود و برای Render صفحه نمایش WPF استفاده میشود.
نکته: همانطور که آشنا شدیم WPF نمیتواند UI Thread را از طریق یک Thread دیگر به روز رسانی کند و یا به عبارتی دیگر یک Thread نمیتواند بصورت مستقیم به اشیایی که توسط Thread دیگر ایجاد شده، دسترسی داشته باشد.
Dispatcher برای این کار دو متد را در اختیار ما قرار میدهد :
متد Invoke : یک Action یا Delegate را میگیرد و متد آنرا به صورت همزمان اجرا میکند. این مورد به این معنا است که تا زمانی که اجرای متد کامل نگردد، عملیاتی صورت نخواهد گرفت و یا به عبارتی دیگر فراخوان را تا زمانیکه زمانبندی به پایان برسد، در حالت مسدود نگهداری خواهد کرد.
مثال :
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); Task.Factory.StartNew(() => { InvokeMethodExample(); }); } private void InvokeMethodExample() { Thread.Sleep(2000); Dispatcher.Invoke(() => { btn1.Content = "By Invoke"; }); } }
مثال :
public MainWindow() { InitializeComponent(); Task.Factory.StartNew(() => { BeginInvokeExample(); }); } private void BeginInvokeExample() { DispatcherOperation op = Dispatcher.BeginInvoke((Action)(() => { btn1.Content = "By BeginInvoke"; })); }