PracticU

Threads - חלק 1

בלי כתוביותעם כתוביות
PracticU › קורס תכנות מתקדם - #Advanced .NET C › Threads - חלק 1
מילון מושגים:
תהליכון - יחידת ביצוע בתוך תהליך, המאפשרת להריץ כמה מהלכים במקביל ולחלוק את אותו זיכרון.
תהליך - מופע של תוכנה שרצה בזיכרון, עם הקוד, הנתונים והמשאבים שהוקצו לה על ידי מערכת ההפעלה.
מעבד - יחידת העיבוד המרכזית של המחשב, שמבצעת את הפקודות של התוכניות ומנהלת את החישובים.
אסינכרוני - אופן פעולה שבו פעולה ארוכה רצה ברקע והתוכנית ממשיכה בלי להמתין לה, ומטופלת כשתסתיים.
אובייקט - יחידה בתכנות מונחה עצמים שמאגדת נתונים (תכונות) ופעולות (מתודות). נוצר בדרך כלל ממחלקה המשמשת כתבנית.
פרמטר - ערך שמועבר לפונקציה או לפקודה כקלט, ומשפיע על אופן פעולתן ועל התוצאה שהן מחזירות.
לולאה - מבנה בקרה שחוזר על קטע קוד שוב ושוב כל עוד תנאי מתקיים או עבור כל פריט באוסף.
.NET - פלטפורמת פיתוח של מיקרוסופט להרצת יישומים בשפות כמו C#, הכוללת ספריות וסביבת ריצה משותפות.

Threads - חלק 1
בשיעור הזה מסבירים מה זה Thread, למה מריצים פונקציה בתהליך נפרד, ואיך מפעילים פונקציה ב-Thread, עוצרים אותה ומודדים את הזמנים.
השלבים העיקריים
1. קריאה סינכרונית: קריאה לפונקציה רצה עד הסוף, והתוכנית ממתינה לה. כשהפונקציה ארוכה, כל התוכנית תקועה מאחוריה.
2. Thread: תהליך נפרד של האפליקציה, שמקבל זמן עבודה מהמעבד במקביל לשאר התוכנית. מערכת ההפעלה מחלקת למעבד פיסות זמן קצרות (time slice) בין התוכניות והתהליכים, ולכן נראה שהכל רץ יחד.
3. הפעלה אסינכרונית: יוצרים אובייקט Thread ומעבירים לו delegate, למשל ThreadStart שמצביע על פונקציה בלי פרמטרים. קריאה ל-Start מכניסה את הפונקציה לתור הריצה, והתוכנית ממשיכה מיד. בשיעור מודגם שהמדידה מראה זמן קצר יותר כשהפונקציות רצות במקביל.
4. פונקציה עם פרמטר: ParameterizedThreadStart מפעיל פונקציה שמקבלת אובייקט אחד, למשל מספר פעמים הריצה, ומבצע בה המרה לטיפוס הנכון.
5. תור ההרצה: מערכת ההפעלה בוחרת את הטרד הבא לרוץ, ומחזירה אותו לסוף התור אחרי פיסת הזמן שלו. אין דרך לכפות על מערכת ההפעלה לתת לטרד זמן מסוים.
6. סיום טבעי: טרד מסתיים כשהפונקציה שהפעיל מסתיימת. אחרי כן לא ניתן להפעיל אותו שוב, ורוצים להפעיל פונקציה מחדש צריך ליצור Thread חדש.
7. עצירה יזומה: Abort הורג את הטרד בלי להמתין לסיום הפונקציה, אבל שיטה מומלצת יותר היא CancellationTokenSource. הפונקציה בודקת את IsCancellationRequested בלולאה, ויוצאת כשמישהו ביקש לבטל.
8. מזהים ושמות: ManagedThreadId הוא מזהה שמוקצה על ידי .NET, ו-Name הוא שם ידידותי לצורך דיבוג. Thread.CurrentThread מאפשר לגשת לטרד שמריץ את הקוד הנוכחי.
בסיום השיעור ברור שטרד מריץ פונקציה אחת במקביל לתוכנית, ושהסיום שלו נקבע לפי סיום הפונקציה, לפי ביטול יזום, או לפי סוג הטרד.
השיעור הבא בקורס: Threads - חלק 2

פרקי ההרצאה: Threads - חלק 1

  1. 00:00

    מהו THREAD

  2. 00:44

    הדגמת הדפסה סדרתית של פונקציות

  3. 03:22

    קריאה סינכרונית ו-Blocking Operation

  4. 04:58

    ריבוי משימות ו-Time Slice

  5. 06:52

    יצירת אובייקט Thread ו-ThreadStart

  6. 08:33

    הרצה אסינכרונית עם Start

  7. 09:54

    מדידת זמנים עם StopWatch

תמלול ההרצאה: Threads - חלק 1

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

  1. 00:01

    אנחנו מגיעים לנושא מאוד נרחב ודי בסיסי שנקרא Threads. פה אנחנו הולכים לראות מה זה Thread, מה אפשר לעשות עם Thread ועוד מספר רב של דברים בנוגע ל-threading.

  2. 00:19

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

  3. 00:56

    ובוא נניח שזאת לא הפונקציה היחידה שיש לנו יש לנו פה עוד פונקציה הפונקציה הבאה בתור תהיה פרינט 2 והיא מדפיסה 20 פעם את המספר 2 אם אנחנו קוראים לפרינט 1 ומיד אחר כך לפרינט 2 בצורה כזאת ברצף אז מה מה שאתם הולכים לראות זה כמובן 20 פעם את המספר 1 ואז 20 פעם את המספר 2. בואו נריץ.

  4. 01:32

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

  5. 02:02

    נפרד של האפליקציה שלנו מבחינת ה-CPU של המחשב. CPU, Central Processing Unit, כלומר המעבד של המחשב. אנחנו יודעים שהמעבד של המחשב מבצע פקודה-פקודה בתוכנית שלנו.

  6. 02:18

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

  7. 02:41

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

  8. 03:09

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

  9. 03:24

    לפי הסדר, יש פה קריאה לפונקציה הראשונה, הפונקציה הזאת מתבצעת עד שהיא מסתיימת, וזה בעצם נקרא Synchronous Call. זה בעצם Blocking Operation. זה אומר שעד שהפונקציה הזאת לא מסתיימת, התוכנית שלנו עסוקה בפעילות של הפונקציה.

  10. 03:53

    גם הפונקציה השנייה זה Synchronous Call. אם אנחנו נדפיס פה end of main אנחנו נראה שהend of main מוצג רק בסיום שתי הפונקציות. בכדי שזה ילך יותר מהר בואו ניקח כל פונקציה ונגיד שהיא רצה רק עשר פעמים, נריץ את זה, נראה שהפונקציה הראשונה בכלל תוקעת לנו את התוכנית, רק כשהיא מסתיימת מתחילה הפונקציה השנייה ואז היא תוקעת לנו את התוכנית

  11. 04:24

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

  12. 04:38

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

  13. 04:56

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

  14. 05:29

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

  15. 05:51

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

  16. 06:06

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

  17. 06:24

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

  18. 06:55

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

  19. 07:22

    לכן אנחנו צריכים להציב על הפונקציה הזאת delegate. delegate אחד שקיים לנו נקרא thread start, נקרא לו ts1. אנחנו יכולים שה-thread start יצביע על print 1 ואז לתת add TS1 ל-thread ולהפעיל אותו.

  20. 07:51

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

  21. 08:12

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

  22. 08:33

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

  23. 08:44

    הפונקציות לא התחילו להתבצע. קריאה לפונקציית סטארט גורמת להרצת הפונקציה פרינט אחת במקרה הזה שהטרנד מצביע עליה אבל בצורה אסינכרונית ויהיה Non-Blocking Operation.

  24. 09:11

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

  25. 09:35

    בואו נריץ ונראה מה יקרה. אני מריץ. על ההתחלה אתם רואים ששתי הקריאות לפונקציות הסתיימו ואנחנו רואים פה את ה-end of main, אפילו אחת מהן התחילה לפניו, וכל הפעילות מתבצעת ביחד, כולל הפעילות של ה-end of main.

  26. 09:58

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

  27. 10:45

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

לקורס המלא: קורס תכנות מתקדם ב‑#C ודוט-נט

ההרצאה הקודמת: תרגילים על יצירת DLL

ההרצאה הבאה: Threads - חלק 2

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

  • Threads

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

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