تقييم الموضوع :
  • 0 أصوات - بمعدل 0
  • 1
  • 2
  • 3
  • 4
  • 5
Windows Message Queue & Message Pumping(Deep Threading 2)
#1
كاتب الموضوع : SHADY_20075

نواصل سلسلة deep
ومع متابعة الدرس الThreading


عند تنفيذ هذا الكود السابق سنفاجأ بتوقف البرنامج عن العمل و ظهور Exception يوضح لنا أنه لا يُمكن لل
Thread الجديدة أن تُعدل في أي Controls أنشأتها الParent Thread !!!!!!!!!!!!


هل هذا يعني أنه لا يمكن للChild Thread أن تغير في أي Control على الForm ؟
بالطبع لا, يمكن للthread أن تقوم بتغيير الControls و لكن قبل أن نشرح الطريقة يجب أولاً أن نُعطي نبذة صغيرة عن

Windows Message Queue & Message Pumping


عندما تقوم بانشاء و تشغيل أي مشروع من أي نوع فان هناك ما يُسمى الMain Thread و هي الthread التي تقوم بتنفيذ ال Main function و هي التي تتولى تنفيذ العمليات الموجودة في البرنامج ...
و لكن
طبيعة البرامج ذات الواجهات الرسومية GUI Application مثل Windows Forms تتميز بأن المستخدم يستطيع أن يتفاعل مع الForm أو البرنامج بشكل عام و في نفس الوقت يقوم البرنامج بتنفيذ هذه الأوامر بالاضافة الى الحفاظ على شكل الForm أي أنه يقوم برسمها مرة بعد أخرى ...
هنا يقوم الWindows بتحويل جميع العمليات و الأوامر على الForm الى ما يُسمى Messages و يضعها بترتيب حدوثها في الMessage Queue حتى يتم تنفيذها و يتم تنفيذ هذه العملية عن طريق الMessage Pumping بأن يقوم البرنامج بعد التشغيل بتشغيل Module يقوم بعمل loop على جميع الMessages داخل الMessage Queue و ينفذ الأوامر المتعلقة بكل Message مثل الضغط على زر أو تحريك المؤشر و هكذا يمكننا تخيل عملية الMessage Pumping كأنها على هذا الشكل



كود :
Message message = null;
while ((message = GetNextMessage()) != null)
{
ProcessMessage(message);
}
و هكذا يستمر البرنامج في العمل حتى تنتهي كل الMessages أو تأتي Message لانهاء البرنامج نفسه ...
و هنا نسأل أين تُنفذ عملية ال Message Pumping ؟؟؟؟
الجواب بسيط في الMain Thread و الدليل أنه اذا تسببت احد الMessages التي يستغرق تنفيذها بعض الوقت الى تجمد الشاشة و هذا لأن الMain Thread مشغولة بتنفيذ الMessage و لا يوجد لديها وقت لاعادة رسم الForm ....

سؤال اخر متى يتم تشغيل الMessage Pumping ؟؟؟
يتم تشغيل الMessage Pumping مع تنفيذ هذه الinstruction الموجودة في كل Windows App و هي
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->


كود :
Application.Run(new AnyFormYouWant());



و الان بعد أن أخذنا نبذة عن الMessage Queueing دعونا نغوص أكثر داخل الWindows Messaging in Win32
ماذا كنا نفعل أيام الWin32 API's لتغيير خاصية في Form أو أي شئ من هذا القبيل؟؟
كنا نملك 2 API's و هما
SendMessage
PostMessage
و كلٍ منهما له استخدام أمثل و هنا يجب أن نعرف الفرق بينهما أولاً
SendMessage تقوم بوضع Message جديدة داخل الMessageQueue و تنتظر الى أن تقوم عملية الMessage Pumping بتنفيذ هذه الMessage حتى تنتهي [Blocking Send] و هي الطريقة الأنسب لتغيير الخواص من ال Main Thread و لكنها ستفشل عند استخدامها من Thread أخرى لتغيير خواص Control أنشأه الMain Thread
PostMessage تقوم أيضاً بوضع Message جديدة داخل الMessage Queue و لكنها لا تنتظر حتى ينتهي تنفيذ الMessage أو حتى يبدأ التنفيذ فهي تنتهي بمجرد وصول الMessage الى الQueue و هذا يُسمى [Non Blocking Send] و هي تصلح في حالة الChild Thread تريد تغيير خواص في Control أنشأه الMain Thread لأنها أشبه بترك رسالة من الChild Thread الى الMain Thread حتى تقوم الMain Thread أثناء عملية الPumping بتنفيذها.. اي في النهاية الMain Thread هي التي قامت بالتغيير

و الان نعود مرة أخرى الى الDot Net
ماذا يحدث عندما يتم تنفيذ عملية مثل
myForm.Height = 100;
يقوم الJIT Compiler بتحويل هذا الأمر الى SendMessage و يتم عندها ارسال Message لتغيير طول الForm و توضع في الQueue حتى يتم تنفيذها في الMessage Pumping و هذه العملية تنجح فقط اذا كانت الMain Thread و التي أنشأت الForm منذ البداية هي التي قامت بعمل تغيير الخاصية...
و لكن ماذا اذا أردت أن استخدم Post Message ؟؟؟؟ هل يمكنني ذلك؟
بالطبع يمكنك ذلك عن طريق استخدام BeginInvoke أو Invoke الموجودتين في كل الControls و في هذه الحالة يتم ارسال Message الى الQueue و لا ننتظر انتهاء تنفيذها فوراً... و هذا هو ما نحتاجة حتى تستطيع الChild Threads تغيير خصائص الForm أو الControls

و الآن نعود الى نفس الCode الذي كتبناه في أول المقال و الذي أردنا به تغيير Label Text باستخدام Thread أخرى...

<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->

كود :
private delegate void ChangeDelegate();
public void InitThread()
{
Thread thread = new Thread(new ThreadStart(ChangeText));
thread.Start();
}

private void ChangeText()
{
if(this.InvokeRequired)
{
this.BeginInvoke(new ChangeDelegate(ChangeText), null);
return;
}
this.lblTest.Text = "Changed";
}
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
و الان بعض التعديلات ظهرت على الكود و في هذه المرة الكود سيعمل طبيعياً و سأشرح ماتم تعديله فالمره القادمه انشاء الله

واسالكم صالح الدعاء وشكرا
}}}
تم الشكر بواسطة:



التنقل السريع :


يقوم بقرائة الموضوع: بالاضافة الى ( 1 ) ضيف كريم