
InvokeRequired. אם הערך true, הפונקציה רצה בטרד אחר. לכן יוצרים MethodInvoker או Action ומעבירים אותו ל-Invoke של הטופס.הטקסט המלא של הדקות הראשונות בהרצאה «Asynchronous - Async Call» בקורס הווידאו של PracticU – כתוביות בעברית עם זמן התחלה לכל פסקה. כך תוכלו לקרוא ולחפש את הנושאים לפני שמתחילים ללמוד.
מגיעים לנושא האסינכרוניות כלומר איך לקרוא לסרוויס שלנו בצורה אסינכרונית קודם כל למה אנחנו צריכים לעשות את זה אז אם אני כרגע מריץ את האפליקציה ואני מכניס ערכים יש לי מיידית תשובה ולא קראתי בצורה אסינכרונית לשירות שלי והכל עובד בסדר מתי אני כן צריך לקרוא בצורה אסינכרונית לשירות
אז בוא נניח שאנחנו מגיעים שוב פעם לשירות עצמו ולמחלקה שמממשת אותו ואנחנו נמצאים כאן בפונקציית add. ובואו נעשה הדגמה כאילו שהפונקציה הזאת לוקחת הרבה זמן לביצוע.
בואו ניקח כאן thread.slip ונדמה כאילו לשירות לוקח 5 שניות להתבצע. עכשיו נסגור את זה ונריץ שוב פעם את כל הפרויקט ונלחץ על הלחצן אחרי שנכניס מספרים ופתאום אני רואה שהטופס שלי תקוע ואני מנסה אפילו להזיז אותו ואני לא מצליח ובמשך 5 שניות הוא לא פעיל וזה מצב שיכול להיות, יכול להיות שהשירות שלי עמוס, יכול
להיות שלוקח לו הרבה זמן להתבצע, שהוא מוציא נתונים ממסד נתונים ולוקח לו פשוט הרבה זמן ובמהלך הזמן הזה שהוא מתבצע, כל פעם שאני לוחץ על הכפתור ואני מוציא אותו לפועל, אני לא רוצה שללקוח הטופס ייתקע ושימו לב שכרגע הוא תקוע בלי שום יכולת לכתוב שוב בתיבות טקסט, בלי שום יכולת להזיז את הטופס ודבר כזה זה
לא דבר הכי נעים בשביל הלקוח, הוא לא מבין מה קורה, הוא חושב שהכל תקוע לו. אנחנו צריכים לשחרר לו את הטופס ולאפשר לשירות לעבוד בצד של השרת, וכאשר השירות מסתיים אנחנו רוצים להציג את התוצאה בלי לתקוע את הטופס במהלך הפעילות של השירות.
אז כרגע עשינו הדגמה של השירות לוקח 5 שניות להתבצע, הוא יכול גם לקחת יותר, הוא יכול גם לקחת פחות, אבל זה מספיק בשביל להעביר את הרעיון של איך עושים את זה. אז קודם כל הפעילות הזאת מתבצעת בצד של הלקוח, ואנחנו הולכים לראות מספר דרכים לבצע את זה.
דבר ראשון, אני רואה שיצרתי כאן בצד של הלקוח אובייקט מהשירות, לאחר מכן קיבלתי פרמטרים מתיבות הטקסט, לאחר מכן קראתי לפונקציית השירות, הפונקציה הזאת לוקחת לה להתבצע הרבה זמן, ולבסוף הצגתי את התוצאה.
אז שיטה ראשונה לבצע קריאה אסינכרונית היא לבצע את כל הסיפור הזה בעצמנו בטרד נפרד. אוקיי, אז בואו נבנה טרד. אז יש לנו פה private void call service וזאת פונקציה נפרדת שאנחנו רוצים שכל הפעילות תתבצע בפונקציה הנפרדת הזאת וכל מה שאנחנו צריכים לעשות זה פשוט לקרוא לפונקציה על ידי thread משלנו אז בואו ניקח כאן
אובייקט thread נקרא לזה service thread שווה new וניצור אובייקט תרד חדש וניתן לו את השם של הפונקציה ServiceTread.IsBackground נקודה מאוד חשובה להגדיר כלומר אם התוכנית נסגרת האם אנחנו רוצים שהטרד הזה ימשיך להיות פעיל אז אם הטרד הזה נניח שולח לנו מייל ואנחנו רוצים שגם אם הלקוח סוגר את התוכנית עדיין המייל יישלח
אז לא הייתי מגדיר אותו כIsBackground.IsBackground מציינת סך הכל שכאשר התוכנית נסגרת וכל הטרדים האחרים בתוכנית שלא מוגדרים כ-Ease Background נגמרים, אז הטרד שלנו אוטומטית יסתיים, הוא לא ימתין לאף טרד אחר.
אז בואו נגדיר אותו כ-True. אם הייתי רוצה שהטרד הזה ימשיך למרות שכל הטרדים האחרים שלא מוגדרים כ-Ease Background מסתיימים, אז הייתי משאיר את זה ב-Default שזה False.
ומיד לאחר מכן, ServiceTrade.Start אני מתחיל את הטרד. אוקיי, בואו נראה איך עכשיו זה משפיע על האפליקציה שלנו.
אז אנחנו מריצים את זה, אנחנו מכניסים ערכים, פוצעים על הלחצן, 5 שניות התוכנית מתבצעת אבל אתם רואים שהטופס לא תקוע ואני יכול להמשיך ולרשום בתוך תיבות הטקסט מה שאני רוצה.
אוקיי, בואו נסתכל על זה, יש לנו פה 4, 4, יש לנו 5, 5, Add, הטופס פנוי ורק כאשר הוא מסתיים פתאום אני מקבל תשובה מהשר"ט. אז זאתי שיטה אחת אבל התשובה שקיבלתי הוצגה ב-Message Box.
מה היה קורה אם התשובה שקיבלתי הייתה מוצגת בלייבל על הטופס? אז בואו ניקח כאן לייבל בשביל להבין מה בעצם המשמעות של הצגת התשובה בלייבל במקום במסג'בוקס.
אז ניקח את הטולבוקס, נציב פה לייבל, נקרא ללייבל הזאת lbl-result, ננקה אותה מהטקסט, ויש לי כרגע לייבל שמסוגלת להציג לי את התוצאה.
אוקיי, אז בואו ניגש ללייבל הזאת, במקום להציג את התשובה במסג' בוקס נבצע את הדבר הבא lbl.result.text = string.format ונכתוב בתוכה את הדבר הבא נפרמט פלט ונגדיר ש-num1 ועוד num2 שווה ל-result אוקיי אז בואו נריץ את זה עדיין יש לנו פעילות של 5 שניות בצד של השרת ונגדיר 1,1 ועוד 2,2
נבצע add אני רואה שהטופס פנוי ואחרי 5 שניות התשובה מוצגת לי בלייבל 1, 1 ועוד 2, 2 שווה 3, 3. אז נראה שהכל בסדר אבל האמת שזה ממש לא בסדר.
למה? מפני שכשאני הרצתי את הפרויקט כרגע אני השתמשתי ב-Ctrl-F5 בשביל להריץ את הפרויקט ו-Ctrl-F5 בעצם אומר שאנחנו מריצים את קובץ ה-Execution בדיוק כמו שהלקוח מריץ את קובץ ה-Execution.
אז למה זה בדיוק משנה? מפני שמה שקרה כרגע זה שה-thread רץ בתהליך נפרד מאשר-thread שמנהל את הטופס. כל טופס מכיל-thread שמנהל אותו אישית. ואם-thread שאנחנו בנינו שמריץ את הפונקציה הזאת כמובן נוגע ב-UI של הטופס זה אומר שכאשר הוא עושה את זה הטופס בעצם לא פעיל.
מפני שיכול להיות שיש לנו איזשהו CPU אחד, וגם אם יש לנו כמה CPUs, יכול להיות שגם הטרד של הטופס וגם הטרד של ה-CPU שלנו, הטרד של הטופס וגם הטרד שאנחנו בנינו יותר נכון, רצים על אותו CPU.
זה אומר שכאשר הטרד שלי פעיל, הטרד של הטופס לא פעיל, ומי שמותר לו לעדכן גרפיקה של הטופס זה רק הטרד של הטופס, כלומר אותו טרד שיצר את רכיבי הטופס.
והבעיה הזאת נקראת Cross-Thread, והיא יכולה להוות המון בעיות, בעיקר בעיות של user interface ומאוד מאוד חשוב להימנע ממנה. אז קודם כל בואו נראה מה היה קורה אם אני הייתי מריץ את הפרויקט לא על ידי Control-F5 אלא על ידי F5. לצורך כך בואו ניקח את ה-Consol Host שלנו, נריץ אותו על ידי Control-F5 והוא רץ,
אוקיי, נוריד אותו פה למטה ואז נחזור ל-WinForm ואת ה-WinForm נריץ על ידי F5. אנחנו הולכים ל-Debug ו-Start New Instance וזה מריץ אותו על ידי F5. F5 אומר אני מריץ את זה בצורה של דיבאג, וזה אומר שאני לא באמת מריץ את קובץ האקסקיושן כמו שהלקוח מריץ, אלא אני מריץ אותו בצורה דיבאג דרך ויז'ואל
סטודיו. ואז ויז'ואל סטודיו יכולה לאתר האם קורה מצב של קרוסט רד אקספצ'ן, ובמקרה, וזה קורה, היא לא תאשר לנו לעשות את זה אלא תעלה חריגה של קרוסט רד אקספצ'ן.
אז בואו נסתכל. נכתוב 112 נלחץ על add, קודם כל אנחנו רואים שה-thread עובד וה-tופס משוחרר, עד כאן הכל בסדר, אבל ברגע שאנחנו רוצים לקבל את התוצאה, שימו לב מה הולך לקרות, נמתין לתוצאה, ואנחנו כבר רואים שמשהו לא בסדר קורה כאן, והופה, התוכנית קרסה.
אנחנו רואים שהוא כותב לי cross-thread operation not valid, ובואו נמשיך לקרוא, נראה control lbl-result access, כלומר, ניגשו אליו From a Thread Other than the Thread it was created on.
זה באמת בדיוק מה שאמרנו. ה-Label LBL Result נוצרה ב-Thread של הטופס, ו-Thread אחר מנסה לגשת אליה. זאת אומרת, Visual Studio לא מאשרת לנו לעשות את הדבר הזה על ידי הרצה של F5, ואם אנחנו מריצים את זה על ידי ה-Control F5,
כאילו שהלקוח מריץ את זה, היא מעלימה עין ואומרת, טוב, נו, אם כבר ללקוח יש את האפליקציה, לא נגרום לו לקריסה אם קרה את הדבר הזה מפני שהנזק שיכול לקרות מזה זה בעיות יחסית מינוריות שקשורות ל-UI ובואו נעלים עין ויאללה לא נגרום לו לקריסה בגלל שאולי הוא לא יראה את הלייבל טוב אולי הוא יראה רק חצי לייבל אולי הוא יראה רק
חצי מידע נצווה וחצי מידע לא נצווה וכל מיני דברים מוזרים שיכולים להיות עם ה-UI אז בגלל זה מעלימים עין דרך אגב ב-WPF בגרסה החדשה של ווינפורמס WPF הם כבר ביטלו את האופציה הזאת של להעלים עין וגם אם אני מריץ את זה בקונטרול F5 וגם אם אני מריץ את זה ב-F5 המערכת אומרת לי דצית אסור לבצע קרוס טרד אקספשן בלי קשר אסור לעשות את זה וצריך להימנע
מהסיפור הזה אז איך אנחנו בעצם מטפלים בדבר הזה אז בואו נבצע כמה דברים בתוך הפונקציה של קול סרוויס אז מה שאנחנו הולכים לעשות אנחנו הולכים לקרוא לכל סרוויס גם דרך הטרד בדיוק