PracticU

Threads - חלק 4

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

Threads - חלק 4
בשיעור הזה נכיר את ה-Thread Pool, מאגר טרדים מוכנים לריצה, ונבין מה היתרון בשימוש בו. אחר כך נעבור לבעיית הקטע הקריטי ולדרכים להגן עליו בעזרת נעילה.
מאגר הטרדים
1. טרדים מוכנים: במקום ליצור אובייקט טרד בכל פעם, המערכת מחזיקה מאגר של טרדים שכבר נוצרו. מספיק לתת לאחד מהם פונקציה ולהגיד לו להתחיל.
2. שחרור אוטומטי: כשטרד נכנס ל-Sleep הוא משתחרר וחוזר למאגר, וטרד אחר יכול להשתמש בו. כשההשהיה מסתיימת המערכת מחזירה טרד לפונקציה שהפעיל.
3. הפעלה: קוראים ל-ThreadPool.QueueUserWorkItem ומעבירים לו שני דברים: הפונקציה שהטרד יריץ, והפרמטר שיישלח אליה.
4. טרדים ברקע: כל הטרדים במאגר הם Background Threads. לכן כשה-Main מסתיים התוכנית נסגרת והטרדים נהרסים איתה. כדי לראות את הפלט מוסיפים Console.ReadLine שמונע את הסגירה.
קטע קריטי ונעילה
5. קטע קריטי: קטע קוד שניגש למשאב משותף לכמה טרדים, למשל משתנה גלובלי, קובץ או מסך. אם כמה טרדים ניגשים אליו בלי הגנה, הפעולות מתערבבות.
6. הדגמה: רשימה של 200,000 מקומות, ושני טרדים שמוסיפים כל אחד מספרים רבים. בלי הגנה הרשימה מכילה פחות ערכים ממה שהוכנס, כי פעולת Add של טרד אחד לא מסתיימת לפני שהשני נכנס אליה.
7. Monitor.Enter ו-Monitor.Exit: נועלים את האובייקט הקריטי ולא את כל הפונקציה. כך רק החלק הקריטי רץ בטור, והשאר של הפונקציה יכול לרוץ במקביל.
8. דגל נעילה ייעודי: מומלץ ליצור אובייקט Object פרטי ולנעול אותו, ולא את האובייקט שעובדים איתו. כך אין סיכוי שקוד אחר ינעל אותו ממקום אחר.
9. בעיית ה-Abort: אם טרד מתבטל אחרי Enter ולפני Exit, המנעול נשאר נעול. הפתרון הוא דגל בוליאני שמסומן אחרי הנעילה, ושחרור ב-finally שמתבצע רק כשהדגל מסומן.
10. פקודת lock: lock מבצעת את התבנית הזאת אוטומטית. בגרסאות 1.0 עד 3.5 הפקודה נעלה בלי הגנה כזאת, ובגרסה 4.0 ומעלה הבעיה טופלה, ולכן מומלץ לעבוד מ-4.0 ומעלה.
כעת אפשר לזהות קטע קריטי בקוד ולהגן עליו בנעילה שמשתחררת תמיד.
השיעור הבא בקורס: Threads - חלק 5

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

  1. 00:00

    מהו ThreadPool ויתרונותיו

  2. 02:16

    QueueUserWorkItem ופונקציית DoWork

  3. 05:16

    העברת פרמטר ל-Thread במאגר

  4. 06:13

    סיכום יכולות ה-Threads

  5. 07:27

    Critical Section ומשאב משותף

  6. 08:11

    תוכנית Locking עם List משותף

  7. 10:35

    הרצת הפונקציה בשני Threads

  8. 13:38

    תוצאה שגויה בגישה מקבילית

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

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

  1. 00:05

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

  2. 00:20

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

  3. 00:52

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

  4. 01:15

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

  5. 01:36

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

  6. 02:04

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

  7. 02:22

    הפונקציה הזאת אומרת לנו תביאו לי פונקציה בואו נפתח פה איזה שהיא סטטיק void do work הפונקציה הזו צריכה לקבל איזשהו אובייקט ואנחנו יכולים עכשיו לתת את do work לפונקציה quser work item בואו ניגש ל do work נעשה פה איזושהי פעילות נרוץ עד 10 עצות ונדפיס פה בואו נדפיס פה אפילו את

  8. 03:07

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

  9. 03:43

    או, הנה, פה אנחנו רואים אפילו הדפסה אחת, את אפס. ומעבר לזה אנחנו לא רואים כלום. למה? כי כל הטרדים בטרד פול הם אוטומטית background threads.

  10. 03:58

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

  11. 04:19

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

  12. 04:39

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

  13. 05:15

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

  14. 05:30

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

  15. 05:56

    ו-ב' פרמטר אחד שהולך להישלח לפונקציה. בואו נריץ את זה ואנחנו רואים שיש לנו פה ריצה של שתיהם ובאמת ה-Trade השני רץ 20 פעם ולא 10 פעמים.

  16. 06:11

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

  17. 06:39

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

  18. 06:55

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

  19. 07:07

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

  20. 07:30

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

  21. 08:00

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

  22. 08:32

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

  23. 09:01

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

  24. 09:26

    ניקח פה פונקציה שנקרא לה add numbers, נרוץ 100,000 פעם בלבד, ניגש ל-numbers.add ונוסיף איזשהו מספר רנדומלי כלשהו.

  25. 09:51

    כעת, אם אנחנו הולכים וקוראים ל-add numbers פעמיים ואז מציגים את numbers.count, כלומר, מה בעצם קיים לנו שם? בואו נריץ אותה אפילו פעם אחת קודם כל.

  26. 10:11

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

  27. 10:33

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

  28. 11:12

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

  29. 11:42

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

  30. 12:09

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

  31. 12:36

    יש פה 1, 2, 3, 4, 5, 6, 7. זאת אומרת שיש פה 20 מיליון מקומות מוכנים בתוך הליסט. אנחנו רצנו על 10 מיליון מקומות בתוך הלולאה. אני מפעיל את הפונקציה בצורה טורית רגילה פעם אחת, ואמור להיות לי כאן מיליון ערכים בתוך הליסט, ואכן יש לנו, לא

  32. 13:09

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

  33. 13:23

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

  34. 13:38

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

  35. 14:00

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

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

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

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

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

  • Threads

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

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