PracticU

Threads - חלק 3

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

Threads - חלק 3
בשיעור הזה מסבירים את מחזור החיים של הטרד במערכת ההפעלה, איזה מצבים יש לו (Runnable, Running, Sleep), ואיך מוציאים טרד למצב המתנה בלי לבזבז מעבד.
השלבים העיקריים
1. אזור Runnable: טרדים שמחכים לתור הריצה. אחרי Start טרד נכנס לכאן.
2. אזור Running: הטרד שמקבל את פיסת הזמן מהמעבד. כשפיסת הזמן נגמרת, הוא חוזר ל-Runnable, וממשיך מאותה פקודה שבה עצר.
3. סיום: טרד מת לאחר שהפונקציה שלו מסתיימת. זה המחזור הטבעי.
4. אין שליטה על התור: מערכת ההפעלה קובעת מי רץ ומתי, ואי אפשר לכפות עליה זמן מעבד לטרד מסוים.
5. מצב Sleep: טרד במצב שינה לא מקבל זמן מעבד ולא חוזר לתור במשך הזמן שנקבע. זה יעיל יותר מלולאה ריקה שמסובבת את המעבד.
6. Thread.Sleep: מוציא את הטרד הנוכחי, כלומר הטרד שהריץ את הפקודה, למצב שינה למשך מספר מילישניות. בשיעור מוכח שלולאה עם המתנה ידנית מבזבזת מעבד, ושאין בה דיוק בזמן כי הוא תלוי במחשב.
7. Suspend ו-Resume: שיטות ישנות שמשעות טרד ומחזירות אותו. הן לא מומלצות, כי טרד שהושעה נשאר שם עד שמישהו מחזיר אותו, ואם הקוד נכשל לפני ה-Resume התוכנית עלולה להיתקע לגמרי.
8. Join: הטרד שקורא ל-Join הופך לממתין עד שהטרד שנקרא לו מסתיים. אפשר לציין זמן מקסימלי, ואז ממשיכים גם אם הטרד השני לא סיים. התוצאה היא הרצה בזה אחר זה, בלי ריצה מקבילית.
בסיום השיעור ברור איך טרד עובר בין המצבים, ושההמתנה הנכונה נעשית ב-Sleep או ב-Join ולא בלולאה.
השיעור הבא בקורס: Threads - חלק 4

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

  1. 00:00

    אזורי Runnable ו-Runtime

  2. 01:35

    קבלת זמן CPU ו-Time Slice

  3. 04:34

    אזור ה-SLEEP ויתרונו

  4. 08:32

    הפקודה Thread.Sleep

  5. 11:38

    למה Sleep יעיל מלולאת המתנה

  6. 13:35

    Suspend ו-Resume

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

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

  1. 00:05

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

  2. 00:43

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

  3. 01:01

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

  4. 01:35

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

  5. 01:54

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

  6. 02:12

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

  7. 02:34

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

  8. 02:52

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

  9. 03:22

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

  10. 03:55

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

  11. 04:07

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

  12. 04:39

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

  13. 05:25

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

  14. 06:01

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

  15. 06:38

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

  16. 07:03

    לדוגמה מהCancellation Token הזה, לדוגמה נדפיס רק את הספרה אחת, בלי ה-thread name, נבצע גם כאן את הפעולה הזאת. נדפיס רק את 2.

  17. 07:31

    בואו נגיד שאנחנו רצים פה עד 10, וגם פה נרוץ עד 10. נעשה את זה simple. ניקח שני אובייקטי תרד פשוטים. הנה תי אחת.

  18. 07:53

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

  19. 08:24

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

  20. 08:42

    תרד.סליפ מקבלת מילי שניות, מילי סקנדס, בואו נניח ניתן 3 שניות ו-3,000 מילי סקנדס, ונבין מי בדיוק הולך לצאת לאזור הסליפ שראינו.

  21. 09:01

    והתשובה מי הולך לצאת לאזור הסליפ היא הטרד הנוכחי. מה זה הטרד הנוכחי? הטרד הנוכחי זה הטרד שהריץ פקודה זאת. מי הריץ את הפקודה הזאת?

  22. 09:21

    האם T1 הריץ את הפקודה? T2? לא ולא. מי שהריץ את הפקודה הזאת זה ה-Main Thread. אם היינו לוקחים את הפקודה הזאת ומעתיקים אותה לכאן, אז מי שהולך להריץ אותה עכשיו זה יהיה T1 והוא ייכנס לסליפ.

  23. 09:44

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

  24. 10:02

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

  25. 10:23

    בואו נריץ, נראה ששלוש שניות T1 מתבצע, ואחרי שלוש שניות המיין ממשיך, יוצר את T2 וממשיך לבצע את T2. בואו נראה דוגמה נוספת.

  26. 10:37

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

  27. 10:49

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

  28. 11:07

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

  29. 11:32

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

  30. 11:52

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

  31. 12:06

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

  32. 12:36

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

  33. 13:01

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

  34. 13:19

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

  35. 13:40

    האפשרות הבאה בתור שיש לנו זה t1.suspend וt1.resume. suspend ו-resume זה פונקציות שנחשבות Deprecated,

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

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

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

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

  • Threads

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

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