PracticU

Asynchronous - Async Call

בלי כתוביותעם כתוביות
PracticU › קורס WCF - תקשורת למתקדמים בדוט-נט › Asynchronous - Async Call
מילון מושגים:
אסינכרוני - אופן פעולה שבו פעולה ארוכה רצה ברקע והתוכנית ממשיכה בלי להמתין לה, ומטופלת כשתסתיים.
טופס - אזור בממשק שבו המשתמש מזין נתונים בשדות ושולח אותם, למשל טופס הרשמה או התחברות.
תהליכון - יחידת ביצוע בתוך תהליך, המאפשרת להריץ כמה מהלכים במקביל ולחלוק את אותו זיכרון.
כפתור - רכיב בממשק המשתמש שהמשתמש לוחץ עליו כדי להפעיל פעולה, כמו שליחת טופס או מעבר לדף אחר.
Visual Studio - סביבת פיתוח משולבת (IDE) של מיקרוסופט לכתיבה, ניפוי והרצה של קוד, בעיקר בשפות .NET.
דיבאג - תהליך של איתור, הבנה ותיקון תקלות בקוד, לעיתים בעזרת כלי שמאפשר לעצור ולעקוב אחר ריצת התוכנית.
רכיב - יחידת בניין עצמאית וניתנת לשימוש חוזר בתוך מערכת או ממשק משתמש, שאחראית על חלק מוגדר מהתפקוד.
פרמטר - ערך שמועבר לפונקציה או לפקודה כקלט, ומשפיע על אופן פעולתן ועל התוצאה שהן מחזירות.

Asynchronous - Async Call
השיעור עוסק בקריאה אסינכרונית לשירות WCF, כדי שהטופס לא ייתקע כשהשירות לוקח זמן. ההדגמה מדמה פעולה של חמש שניות באמצעות Thread.Sleep.
הבעיה
כשהפונקציה נקראת ישירות מכפתור, הטופס מוקפא לחמש שניות. המשתמש לא יכול להקליד, להזיז את החלון או ללחוץ על כלום, והוא חושב שהתוכנה נתקעה.
פתרון ראשון: טרד נפרד
1. יוצרים פונקציה נפרדת, CallService, שמכילה את כל הפעולה: יצירת השירות, הקריאה לפונקציה, קבלת התוצאה וסגירה.
2. יוצרים אובייקט Thread שמפעיל את הפונקציה.
3. מגדירים את הטרד עם IsBackground = true. לפי ההסבר בשיעור, כשהתוכנית נסגרת, טרד כזה מסתיים יחד עם כל שאר הטרדים שאינם רקע. ברירת המחדל היא false, והטרד ממשיך לרוץ גם אחרי שהתוכנית נסגרת.
4. מפעילים את הטרד באמצעות Start.
התוצאה: הטופס פנוי בזמן ההמתנה, ובסיום מוצגת התשובה ב-MessageBox.
בעיית Cross-Thread
כשמציגים את התוצאה בלייבל מתוך הטרד הנפרד, התוצאה תלויה באופן ההרצה:
1. Ctrl+F5: הפעלה כמו שמשתמש מפעיל את הקובץ. התוצאה מופיעה, כי Visual Studio לא מסמן את הפעולה.
2. F5: הפעלה במצב דיבאג. Visual Studio זורקת InvalidOperationException עם ההודעה Cross-thread operation not valid, כי רק הטרד שיצר את הרכיב רשאי לעדכן אותו.
הכלל: עדכון רכיבי ממשק חייב להתבצע בטרד של הטופס.
פתרון שני: InvokeRequired ו-Invoke
1. בודקים את InvokeRequired. אם הערך true, הפונקציה רצה בטרד אחר. לכן יוצרים MethodInvoker או Action ומעבירים אותו ל-Invoke של הטופס.
2. ה-Invoke גורם לטופס לקרוא שוב לאותה פונקציה בטרד שלו. שם InvokeRequired הוא false, והעדכון מתבצע בבטחה.
3. משתנים מקומיים נוצרים מחדש בכל קריאה, ולכן מעבירים את הטקסט לפונקציה כפרמטר. בתחילת השיעור השתמשו במשתנים גלובליים, אבל ההסבר מציין שעדיף להימנע מכך.
השיטה עובדת, אבל היא דורשת ניהול טרדים ידני ומורכבת.
השיעור הבא בקורס: Asynchronous - xxxAsync Function

פרקי ההרצאה: Asynchronous - Async Call

  1. 00:00

    למה צריך קריאה אסינכרונית לשירות

  2. 00:57

    הדמיית פונקציה איטית בשירות

  3. 02:31

    שיטה ראשונה: Thread משלנו

  4. 05:06

    הצגת התשובה בלייבל במקום Message Box

  5. 07:03

    Cross-Thread ו-Ctrl-F5 מול F5

  6. 09:26

    שגיאת Cross-Thread בזמן דיבאג

תמלול ההרצאה: Asynchronous - Async Call

הטקסט המלא של הדקות הראשונות בהרצאה «Asynchronous - Async Call» בקורס הווידאו של PracticU – כתוביות בעברית עם זמן התחלה לכל פסקה. כך תוכלו לקרוא ולחפש את הנושאים לפני שמתחילים ללמוד.

  1. 00:20

    מגיעים לנושא האסינכרוניות כלומר איך לקרוא לסרוויס שלנו בצורה אסינכרונית קודם כל למה אנחנו צריכים לעשות את זה אז אם אני כרגע מריץ את האפליקציה ואני מכניס ערכים יש לי מיידית תשובה ולא קראתי בצורה אסינכרונית לשירות שלי והכל עובד בסדר מתי אני כן צריך לקרוא בצורה אסינכרונית לשירות

  2. 00:45

    אז בוא נניח שאנחנו מגיעים שוב פעם לשירות עצמו ולמחלקה שמממשת אותו ואנחנו נמצאים כאן בפונקציית add. ובואו נעשה הדגמה כאילו שהפונקציה הזאת לוקחת הרבה זמן לביצוע.

  3. 01:00

    בואו ניקח כאן thread.slip ונדמה כאילו לשירות לוקח 5 שניות להתבצע. עכשיו נסגור את זה ונריץ שוב פעם את כל הפרויקט ונלחץ על הלחצן אחרי שנכניס מספרים ופתאום אני רואה שהטופס שלי תקוע ואני מנסה אפילו להזיז אותו ואני לא מצליח ובמשך 5 שניות הוא לא פעיל וזה מצב שיכול להיות, יכול להיות שהשירות שלי עמוס, יכול

  4. 01:37

    להיות שלוקח לו הרבה זמן להתבצע, שהוא מוציא נתונים ממסד נתונים ולוקח לו פשוט הרבה זמן ובמהלך הזמן הזה שהוא מתבצע, כל פעם שאני לוחץ על הכפתור ואני מוציא אותו לפועל, אני לא רוצה שללקוח הטופס ייתקע ושימו לב שכרגע הוא תקוע בלי שום יכולת לכתוב שוב בתיבות טקסט, בלי שום יכולת להזיז את הטופס ודבר כזה זה

  5. 01:58

    לא דבר הכי נעים בשביל הלקוח, הוא לא מבין מה קורה, הוא חושב שהכל תקוע לו. אנחנו צריכים לשחרר לו את הטופס ולאפשר לשירות לעבוד בצד של השרת, וכאשר השירות מסתיים אנחנו רוצים להציג את התוצאה בלי לתקוע את הטופס במהלך הפעילות של השירות.

  6. 02:17

    אז כרגע עשינו הדגמה של השירות לוקח 5 שניות להתבצע, הוא יכול גם לקחת יותר, הוא יכול גם לקחת פחות, אבל זה מספיק בשביל להעביר את הרעיון של איך עושים את זה. אז קודם כל הפעילות הזאת מתבצעת בצד של הלקוח, ואנחנו הולכים לראות מספר דרכים לבצע את זה.

  7. 02:35

    דבר ראשון, אני רואה שיצרתי כאן בצד של הלקוח אובייקט מהשירות, לאחר מכן קיבלתי פרמטרים מתיבות הטקסט, לאחר מכן קראתי לפונקציית השירות, הפונקציה הזאת לוקחת לה להתבצע הרבה זמן, ולבסוף הצגתי את התוצאה.

  8. 02:56

    אז שיטה ראשונה לבצע קריאה אסינכרונית היא לבצע את כל הסיפור הזה בעצמנו בטרד נפרד. אוקיי, אז בואו נבנה טרד. אז יש לנו פה private void call service וזאת פונקציה נפרדת שאנחנו רוצים שכל הפעילות תתבצע בפונקציה הנפרדת הזאת וכל מה שאנחנו צריכים לעשות זה פשוט לקרוא לפונקציה על ידי thread משלנו אז בואו ניקח כאן

  9. 03:28

    אובייקט thread נקרא לזה service thread שווה new וניצור אובייקט תרד חדש וניתן לו את השם של הפונקציה ServiceTread.IsBackground נקודה מאוד חשובה להגדיר כלומר אם התוכנית נסגרת האם אנחנו רוצים שהטרד הזה ימשיך להיות פעיל אז אם הטרד הזה נניח שולח לנו מייל ואנחנו רוצים שגם אם הלקוח סוגר את התוכנית עדיין המייל יישלח

  10. 04:01

    אז לא הייתי מגדיר אותו כIsBackground.IsBackground מציינת סך הכל שכאשר התוכנית נסגרת וכל הטרדים האחרים בתוכנית שלא מוגדרים כ-Ease Background נגמרים, אז הטרד שלנו אוטומטית יסתיים, הוא לא ימתין לאף טרד אחר.

  11. 04:19

    אז בואו נגדיר אותו כ-True. אם הייתי רוצה שהטרד הזה ימשיך למרות שכל הטרדים האחרים שלא מוגדרים כ-Ease Background מסתיימים, אז הייתי משאיר את זה ב-Default שזה False.

  12. 04:29

    ומיד לאחר מכן, ServiceTrade.Start אני מתחיל את הטרד. אוקיי, בואו נראה איך עכשיו זה משפיע על האפליקציה שלנו.

  13. 04:39

    אז אנחנו מריצים את זה, אנחנו מכניסים ערכים, פוצעים על הלחצן, 5 שניות התוכנית מתבצעת אבל אתם רואים שהטופס לא תקוע ואני יכול להמשיך ולרשום בתוך תיבות הטקסט מה שאני רוצה.

  14. 04:55

    אוקיי, בואו נסתכל על זה, יש לנו פה 4, 4, יש לנו 5, 5, Add, הטופס פנוי ורק כאשר הוא מסתיים פתאום אני מקבל תשובה מהשר"ט. אז זאתי שיטה אחת אבל התשובה שקיבלתי הוצגה ב-Message Box.

  15. 05:13

    מה היה קורה אם התשובה שקיבלתי הייתה מוצגת בלייבל על הטופס? אז בואו ניקח כאן לייבל בשביל להבין מה בעצם המשמעות של הצגת התשובה בלייבל במקום במסג'בוקס.

  16. 05:28

    אז ניקח את הטולבוקס, נציב פה לייבל, נקרא ללייבל הזאת lbl-result, ננקה אותה מהטקסט, ויש לי כרגע לייבל שמסוגלת להציג לי את התוצאה.

  17. 05:53

    אוקיי, אז בואו ניגש ללייבל הזאת, במקום להציג את התשובה במסג' בוקס נבצע את הדבר הבא lbl.result.text = string.format ונכתוב בתוכה את הדבר הבא נפרמט פלט ונגדיר ש-num1 ועוד num2 שווה ל-result אוקיי אז בואו נריץ את זה עדיין יש לנו פעילות של 5 שניות בצד של השרת ונגדיר 1,1 ועוד 2,2

  18. 06:46

    נבצע add אני רואה שהטופס פנוי ואחרי 5 שניות התשובה מוצגת לי בלייבל 1, 1 ועוד 2, 2 שווה 3, 3. אז נראה שהכל בסדר אבל האמת שזה ממש לא בסדר.

  19. 07:03

    למה? מפני שכשאני הרצתי את הפרויקט כרגע אני השתמשתי ב-Ctrl-F5 בשביל להריץ את הפרויקט ו-Ctrl-F5 בעצם אומר שאנחנו מריצים את קובץ ה-Execution בדיוק כמו שהלקוח מריץ את קובץ ה-Execution.

  20. 07:20

    אז למה זה בדיוק משנה? מפני שמה שקרה כרגע זה שה-thread רץ בתהליך נפרד מאשר-thread שמנהל את הטופס. כל טופס מכיל-thread שמנהל אותו אישית. ואם-thread שאנחנו בנינו שמריץ את הפונקציה הזאת כמובן נוגע ב-UI של הטופס זה אומר שכאשר הוא עושה את זה הטופס בעצם לא פעיל.

  21. 07:53

    מפני שיכול להיות שיש לנו איזשהו CPU אחד, וגם אם יש לנו כמה CPUs, יכול להיות שגם הטרד של הטופס וגם הטרד של ה-CPU שלנו, הטרד של הטופס וגם הטרד שאנחנו בנינו יותר נכון, רצים על אותו CPU.

  22. 08:07

    זה אומר שכאשר הטרד שלי פעיל, הטרד של הטופס לא פעיל, ומי שמותר לו לעדכן גרפיקה של הטופס זה רק הטרד של הטופס, כלומר אותו טרד שיצר את רכיבי הטופס.

  23. 08:19

    והבעיה הזאת נקראת Cross-Thread, והיא יכולה להוות המון בעיות, בעיקר בעיות של user interface ומאוד מאוד חשוב להימנע ממנה. אז קודם כל בואו נראה מה היה קורה אם אני הייתי מריץ את הפרויקט לא על ידי Control-F5 אלא על ידי F5. לצורך כך בואו ניקח את ה-Consol Host שלנו, נריץ אותו על ידי Control-F5 והוא רץ,

  24. 08:47

    אוקיי, נוריד אותו פה למטה ואז נחזור ל-WinForm ואת ה-WinForm נריץ על ידי F5. אנחנו הולכים ל-Debug ו-Start New Instance וזה מריץ אותו על ידי F5. F5 אומר אני מריץ את זה בצורה של דיבאג, וזה אומר שאני לא באמת מריץ את קובץ האקסקיושן כמו שהלקוח מריץ, אלא אני מריץ אותו בצורה דיבאג דרך ויז'ואל

  25. 09:11

    סטודיו. ואז ויז'ואל סטודיו יכולה לאתר האם קורה מצב של קרוסט רד אקספצ'ן, ובמקרה, וזה קורה, היא לא תאשר לנו לעשות את זה אלא תעלה חריגה של קרוסט רד אקספצ'ן.

  26. 09:24

    אז בואו נסתכל. נכתוב 112 נלחץ על add, קודם כל אנחנו רואים שה-thread עובד וה-tופס משוחרר, עד כאן הכל בסדר, אבל ברגע שאנחנו רוצים לקבל את התוצאה, שימו לב מה הולך לקרות, נמתין לתוצאה, ואנחנו כבר רואים שמשהו לא בסדר קורה כאן, והופה, התוכנית קרסה.

  27. 09:48

    אנחנו רואים שהוא כותב לי cross-thread operation not valid, ובואו נמשיך לקרוא, נראה control lbl-result access, כלומר, ניגשו אליו From a Thread Other than the Thread it was created on.

  28. 10:05

    זה באמת בדיוק מה שאמרנו. ה-Label LBL Result נוצרה ב-Thread של הטופס, ו-Thread אחר מנסה לגשת אליה. זאת אומרת, Visual Studio לא מאשרת לנו לעשות את הדבר הזה על ידי הרצה של F5, ואם אנחנו מריצים את זה על ידי ה-Control F5,

  29. 10:23

    כאילו שהלקוח מריץ את זה, היא מעלימה עין ואומרת, טוב, נו, אם כבר ללקוח יש את האפליקציה, לא נגרום לו לקריסה אם קרה את הדבר הזה מפני שהנזק שיכול לקרות מזה זה בעיות יחסית מינוריות שקשורות ל-UI ובואו נעלים עין ויאללה לא נגרום לו לקריסה בגלל שאולי הוא לא יראה את הלייבל טוב אולי הוא יראה רק חצי לייבל אולי הוא יראה רק

  30. 10:48

    חצי מידע נצווה וחצי מידע לא נצווה וכל מיני דברים מוזרים שיכולים להיות עם ה-UI אז בגלל זה מעלימים עין דרך אגב ב-WPF בגרסה החדשה של ווינפורמס WPF הם כבר ביטלו את האופציה הזאת של להעלים עין וגם אם אני מריץ את זה בקונטרול F5 וגם אם אני מריץ את זה ב-F5 המערכת אומרת לי דצית אסור לבצע קרוס טרד אקספשן בלי קשר אסור לעשות את זה וצריך להימנע

  31. 11:17

    מהסיפור הזה אז איך אנחנו בעצם מטפלים בדבר הזה אז בואו נבצע כמה דברים בתוך הפונקציה של קול סרוויס אז מה שאנחנו הולכים לעשות אנחנו הולכים לקרוא לכל סרוויס גם דרך הטרד בדיוק

לקורס המלא: קורס WCF אונליין בדוט-נט

ההרצאה הקודמת: Classic Web Service

ההרצאה הבאה: Asynchronous - xxxAsync Function

הרצאות קשורות מקורסים אחרים

  • הצורך ב-Async Task
  • Async Custom Validators
  • שימוש ב-Async Controller
  • הסבר של async ועל await

קורסים נוספים במסלול

  • קורס WinForms - תוכנות רגילות בווינדוס
  • פרויקט מעשי ב-#C - בונים מחשבון פשוט
  • פרויקט מעשי ב-#C - ספר טלפונים
  • פרויקט מעשי ב-#C - תוכנת צייר
  • קורס ADO.NET - חיבור אתר למסד נתונים
  • קורס תכנות מתקדם - #Advanced .NET C