Asynchronous Delegates

סגור באמצעות טופס זה תוכלו לספר ולהמליץ לחבריכם..
שם השולח:
כתובת דוא"ל של השולח:
שם המקבל:
שלח לכתובת דוא"ל:
הוסף הערה:
מאמר קצר המסביר ומדגים Asynchronous Delegates.
 
 
Asynchronous Delegates
 
מאת: ארז קלר

הורדת דוגמאות קוד


כברירת מחדל delegates מופעלים סינכרונית:
 1  : using System; 
 2  : using System.Collections.Generic; 
 3  : using System.Linq; 
 4  : using System.Text; 
 5  : using System.Threading.Tasks; 
 6  :  
 7  : namespace DelegateSample01 
 8  : { 
 9  :     public delegate void MyDelegate(); 
 10 :     class Program 
 11 :     { 
 12 :         static void Main(string[] args) 
 13 :         { 
 14 :             MyDelegate my_delegate = new MyDelegate(Func); 
 15 :             //MyDelegate my_delegate2 = Func; 
 16 :             my_delegate(); 
 17 :             Console.WriteLine("Main - Thread ID: {0}", System.Threading.Thread.CurrentThread.ManagedThreadId); 
 18 :         } 
 19 :         public static void Func() 
 20 :         { 
 21 :             Console.WriteLine("Delegate - Thread ID: {0}", System.Threading.Thread.CurrentThread.ManagedThreadId); 
 22 :         } 
 23 :     } 
 24 :  
 25 :  
 26 : } 
שורה 9: הגדרת delegate
שורה 14: הגדרת והקצאת מופע מה- delegate.
MyDelegate my_delegate = new MyDelegate(Func);
my_delegate מוקצה ומיוחס למתודה Func , בשיטת כתיבה אחרת (שורה 15) ניתן היה לכתוב:
MyDelegate my_delegate = Func;
 אין הבדל בין השיטות.

שורה 16:
my_delegate();
הקריאה ל-my_delegate מפעילה את המתודה Func(), היה ניתן להפעיל אותה באמצעות המתודה my_delegate.Invoke(), למעשה זה מה שקורה בפועל.
ההפעלה, היא הפעלה סינכרונית, הקריאה ל- Console.WriteLine(…) בשורה מתחת תבוצע רק לאחר שהמתודה Func תסתיים.
פלט:

 

מהפלט ניתן להבחין בבירור שאין עבודה אסינכרונית, גם Main וגם המתודה Func שהופעלה על ידי ה- delegate מורצים באותו ה-Thread.

לחילופין ניתן להפעיל את ה- delegate אסינכרונית.
delegates אסינכרוניים מנוהלים על ידי ה- ThreadPool.
Delegates אסינכרוניים פועלים על פי מודל שנקרא APM (Asynchronous Programming Model).
APM זו טכניקה להרצה אסינכרונית מתבססת על הממשק IAsyncResult ,  על המחלקה AsyncResult המממשת את הממשק ועל שתי מתודות: BeginInvoke ו- EndInvoke.
BeginInvoke ו- EndInvoke הן מתודות שנוצרות בזמן קומפילציה והחתימה שלהם תלויה בחתימת ה-Delegate.
תוכנה נחמדה וחינמית בשם CodeReflect תגלה לנו את הנסתר מאחורי הקלעים.
CodeReflect יודעת לקחת קוד מקומפל ולהציג את ה- MSIL שלו (Microsoft Intermediate Language).


אם נבדוק ב-CodeReflect את הקוד שכתבנו בדוגמה למעלה נראה את קוד ה- MSIL הבא:
 

במרחב השמות DelegateSample01 מוגדרים שני טיפוסים, הראשון הוא class Program המכיל את המתודה הראשית Main ואת המתודה Func אשר אנחנו מפעילים באמצעות ה- delegate, הטיפוס השני שמוגדר הוא ה- Delegate שנקרא MyDelegate, ניתן לראות שהוא נגזרת של מחלקת הבסיס MulticastDelegate ושיש לו מספר מתודות:
  • Invoke להפעלה סינכרונית של ה-delegate.
  • ו- BeginInvoke ו- EndInvoke לטובת הפעלה אסינכרונית של ה-delegate.
החתימה של BeginInvoke תלויה בחתימה של ה-delegate, הפרמטרים הראשונים זהים לחתימת ה- delegate, מספר הפרמטרים והטיפוסים תלויים אך ורק בחתימת ה- delegate, בנוסף מתווספים עוד שני פרמטרים שעליהם נשוחח בהמשך.
לפרטים נוספים על APM



בדוגמה הבאה נפעיל את ה- delegate אבל הפעם נבצע זאת אסינכרונית:
 1  : using System; 
 2  : using System.Collections.Generic; 
 3  : using System.Linq; 
 4  : using System.Text; 
 5  : using System.Threading.Tasks; 
 6  :  
 7  : namespace DelegateSample02 
 8  : { 
 9  :     public delegate void MyDelegate(string msg, int num); 
 10 :     class Program 
 11 :     { 
 12 :         static void Main(string[] args) 
 13 :         { 
 14 :             MyDelegate del1 = new MyDelegate(Func); 
 15 :             MyDelegate del2 = new MyDelegate(Func); 
 16 :  
 17 :             IAsyncResult result1 = del1.BeginInvoke("Func1", 100, null, null); 
 18 :             IAsyncResult result2 = del2.BeginInvoke("Func2", 100, null, null); 
 19 :             Console.WriteLine("Main - Thread ID: {0}", System.Threading.Thread.CurrentThread.ManagedThreadId); 
 20 :  
 21 :             result1.AsyncWaitHandle.WaitOne(); 
 22 :             result2.AsyncWaitHandle.WaitOne(); 
 23 :         } 
 24 :         public static void Func(string msg, int num) 
 25 :         { 
 26 :             Console.WriteLine("Delegate - Thread ID: {0}", System.Threading.Thread.CurrentThread.ManagedThreadId); 
 27 :         } 
 28 :     } 
 29 : } 

הפעם ה- delegate מקבל שני פרמטרים (לא נעשה בהם שימוש בדוגמה זו אלא רק בדוגמה הבאה).
נקצה שני מופעים מה-delegate כדי שנרגיש תחושה של אסינכרוניות קלילה ונפעיל אותם באמצעות המתודה BeginInvoke.
מכיוון שחתימת ה- delegate  מכילה שני פרמטרים, BeginInvoke מקבל 4 פרמטרים, השניים הראשונים זהים לחתימה של ה- delegate: הראשון מטיפוס string, השני מטיפוס int.
השניים האחרונים חשובים מאוד, אולם אינם מעניינים אותנו כרגע (מאוד יעניינו אותנו בהמשך),
אם חתימת ה- delegate שלנו הייתה מכילה 3 פרמטרים, BeginInvoke הייתה מכילה 5 פרמטרים, השלושה הראשונים בהתאם לחתימת ה- delegate והשניים הנוספים שנשוחח עליהם בהמשך.
ואם חתימת ה-delegate שלנו הייתה מכילה 17 פרמטרים, BeginInvoke הייתה מכילה 19 פרמטרים ..... וכיוצא בזה.
הערך המוחזר מהמתודה הוא מהטיפוס IAsyncResult והוא מכיל ייחוס לתוצאת החישוב של ה- delegate (בדוגמה זו לא נעשה בו שימוש, נשאיר אותו לדוגמאות הבאות).
שתי השורות האחרונות ב- main חוסמות את המשך הריצה של Main עד לסיום ביצוע של שני התהליכים האסינכרוניים.
פלט:

מהפלט ניתן להבחין בבירור ש- Main וכל אחד מה-delegate רצים ב- Thread נפרד.

בדוגמה השלישית נרחיב קצת את היריעה, נתייחס לפרמטר השלישי של BeginInvoke ולמתודה EndInvoke.
 1  : using System; 
 2  : using System.Collections.Generic; 
 3  : using System.Linq; 
 4  : using System.Text; 
 5  : using System.Threading.Tasks; 
 6  :  
 7  : namespace DelegateSample03 
 8  : { 
 9  :     public delegate void MyDelegate(string msg, int num); 
 10 :     class Program 
 11 :     { 
 12 :         static void Main(string[] args) 
 13 :         { 
 14 :             MyDelegate del1 = new MyDelegate(Func); 
 15 :             MyDelegate del2 = new MyDelegate(Func); 
 16 :  
 17 :            IAsyncResult result1 = del1.BeginInvoke("Func1", 10, 
 18 :                             new AsyncCallback(TaskCompleted), null); 
 19 :             IAsyncResult result2 = del2.BeginInvoke("Func2", 10, 
 20 :                             new AsyncCallback(TaskCompleted), null); 
 21 :             del1.EndInvoke(result1); 
 22 :             del2.EndInvoke(result2); 
 23 :             Console.WriteLine("Continue ....."); 
 24 :         } 
 25 :         public static void Func(string msg, int num) 
 26 :         { 
 27 :             for (int i = 0; i <  num; i++) 
 28 :             { 
 29 :                 Console.Write(msg + ": " + i + ", "); 
 30 :                 System.Threading.Thread.Sleep(100); 
 31 :             } 
 32 :         } 
 33 :         public static void TaskCompleted(IAsyncResult R) 
 34 :         { 
 35 :             Console.WriteLine("End Async"); 
 36 :         } 
 37 :     } 
 38 : } 

שורות 18 ו-20: הפרמטר השלישי (במקרה זה כי חתימת ה- delegate מכילה שני פרמטרים) הוא בעצמו delegate מהטיפוס AsyncCallback והוא מופעל כאשר ה- delegate מסיים את פעילותו האסינכרונית.
AsyncCallback מגדיר חתימת מתודה שמחזירה void ומקבלת ייחוס לממשק IAsyncResult, בפועל מתקבל ייחוס לאובייקט מהמחלקה AsyncResult המממש את הממשק.
בדוגמה, המתודה TaskCompleted (שורה 33) תופעל כאשר ה-delegate יסיים את המשימה שלשמה הוא הופעל. במקרה הפשוט שלפנינו נודיע למשתמש על סיום הפעולה האסינכרונית.
המתודה EndInvoke מאפשרת קבלת ערך מוחזר מהמתודה ובנוסף חוסמת את המשך ריצת התוכנית עד לסיום הפעולה האסינכרונית.
פלט:


הדוגמה האחרונה מציגה הפעלת delegate שמופעל אסינכרוני ונדרש להחזיר את תוצאת החישוב שלו.
 1  : using System; 
 2  : using System.Collections.Generic; 
 3  : using System.Linq; 
 4  : using System.Text; 
 5  : using System.Threading.Tasks; 
 6  : using System.Runtime.Remoting.Messaging; 
 7  :  
 8  : namespace DelegateSample04 
 9  : { 
 10 :     public delegate int MyDelegate(int[] arr); 
 11 :     class Program 
 12 :     { 
 13 :         static void Main(string[] args) 
 14 :         { 
 15 :             int[] arr1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 56, 44, 5, 12, 83, 56, 22, 92 }; 
 16 :             int[] arr2 = { 11, 22, 33, 44, 55, 66, 77, 88, 99 }; 
 17 :  
 18 :             MyDelegate del1 = new MyDelegate(Sum); 
 19 :             IAsyncResult result1 = del1.BeginInvoke(arr1, new AsyncCallback(MyCallBack), "A message from thread 1"); 
 20 :              
 21 :             MyDelegate del2 = new MyDelegate(Sum); 
 22 :             IAsyncResult result2 = del2.BeginInvoke(arr2, new AsyncCallback(MyCallBack), "A message from thread 2"); 
 23 :  
 24 :             int sum1 = del1.EndInvoke(result1); 
 25 :             int sum2 = del2.EndInvoke(result2); 
 26 :             Console.WriteLine("Sum1 = {0}, Sum2 = {1}", sum1, sum2); 
 27 :         } 
 28 :          public static int Sum(int[] arr) 
 29 :         { 
 30 :             int sum = 0; 
 31 :             for (int i = 0; i <  arr.Length; i++) 
 32 :             { 
 33 :                 sum += arr[i]; 
 34 :             } 
 35 :             return sum; 
 36 :         } 
 37 :         static void MyCallBack(IAsyncResult async) 
 38 :         { 
 39 :             Console.WriteLine(async.AsyncState.ToString()); 
 40 :         } 
 41 :     } 
 42 : } 
הפעם, MyDelegate מחזיר int, מבצע חישוב ומחזיר תשובה. הפעם EndInvoke מחזיר int (שורות 24 ו-25) בהתאם לחתימה של ה- delegate, הערך המוחזר של EndInvoke זו תוצאת החישוב.
שימו לב למבנה של צמד המתודות האסינכרוניות של ה-delegate. המתודה BeginInvoke שמתחילה את הפעולה האסינכרונית מקבלת את הפרמטרים שמוגדרים בחתימת ה- delegate, מערך של int בדוגמה.
גם EndInvoke מושפעת מהחתימה של ה- delegate, היא מחזירה ערך מהטיפוס שמוגדר כערך המוחזר ב- delegate היא חוזרת רק עם סיום הפעולה האסינכרונית ומחזירה את התשובה.
פלט:

 


כל הזכויות שמורות למחבר ©