
num++ היא למעשה שלוש פעולות: קריאה, חיבור והשמה. טרד אחר יכול להתערב באמצע, ולכן צריך נעילה.Add מוסיף מספר למשתנה. Exchange שם ערך חדש ומחזיר את הישן.Thread.AllocateDataSlot יוצר חריץ זיכרון שלכל טרד יש בו ערך נפרד. בעזרת Thread.SetData ו-Thread.GetData אפשר להעביר את החריץ לפונקציה אחרת ולקרוא ממנו עבור כל טרד בנפרד.Thread.GetNamedDataSlot מאפשר כמה חריצים בתוך אותה פונקציה, לכל אחד שם משלו, למשל positive ו-negative. כך מקבלים עבור כל טרד כמה אזורי זיכרון נפרדים.הטקסט המלא של הדקות הראשונות בהרצאה «Threads - חלק 7» בקורס הווידאו של PracticU – כתוביות בעברית עם זמן התחלה לכל פסקה. כך תוכלו לקרוא ולחפש את הנושאים לפני שמתחילים ללמוד.
עכשיו, לסדרה של רכיבים מהרכיבים שראינו קיימת גרסה שהיא יותר לייט מבחינת זיכרון ומבחינת מהירות. כל רכיב כזה שהוא יותר לייט נקרא סלים, כאשר סלים באנגלית זה רזה.
בואו נסתכל אילו רכיבים קיימים עבורם גרסאות סלים. פתחתי בטעות ווינפורם, בואו נסגור אותו. ונפתח קונסול אפליקיישן.
אוקיי, אז הדבר הראשון שראינו זה סמאפור, וקיים לנו סמאפור סלים. הוא יכול לקבל רק את ה-initial count, אבל בואו ניתן לו אותו הדבר.
מה זה אומר סלים? סלים זה נקרא slim version, זאת אומרת גרסה של אותו רכיב, אבל הוא עובד הרבה יותר מהר ותופס הרבה פחות זיכרון.
אז אם כן, למה לא לעבוד עם ה-slim version? יש מקרה אחד שאנחנו לא יכולים לעבוד איתו עם גרסת הסלים והמקרה הזה כאשר אנחנו רוצים שהאובייקט שלנו יחצה משהו שנקרא App Domain. מה זה App Domain? אז אחד מהסרטונים המאוחרים יותר במודול הזה ידבר על App Domain ו-App Domain זה בעצם מעטפת מסוימת שיש לנו בקוד בתוך ה-Process.
אנחנו נדבר על זה בהרחבה בהמשך אבל זה אומר שאנחנו יכולים לבנות בעצם איזושהי מעטפת שבתוכה רץ הקוד שלנו למעשה אוטומטית קיים לנו מעטפת כזאת ואם אנחנו רוצים לפתוח עוד מעטפת שיש לה מרחבי זיכרון משלה ואזור זיכרון סטטי משלה ועוד כל מיני דברים מעניינים שקורים במעטפת הזאת שאנחנו מריצים אז אין לנו אין לנו בעיה לעשות את זה בדוטנט
אבל אנחנו לא יכולים לעבוד עם סמאפור סלים בתוך מעטפות שונות כאלה. אז יהיה לנו ממש מודול או וידאו נפרד לגבי אפ דומיינס, סמאפור כן עובד וחוצה אפ דומיינס, כל רכיבי הסלימס לא תומכים בעבודה באפ דומיינס.
בואו נסתכל על הדבר הבא, הדבר הבא שראינו היה Manual reset event, ויש עבורו גם את גרסת הסלימס. גם כאן אנחנו רואים שיש לנו גרסת סלימס ורשן שלו.
לאאוטוריסטיטיבט אין כרגע גרסת סלימס. יש לנו ל-ReaderWriterLock גרסה שהיא הרבה יותר מהירה, ואנחנו מדברים על מהירות משמעותית כל פעם שאנחנו מדברים על זה.
רידר רייטר לוק סלימס, נמצא פה את הגרסה שלה, ושוב, כל אלה שהם גרסאות של סלימס הם יותר מהירים אבל לא תומכים באפ דומיינס, לא תומכים בעבודה מרחוק מה שנקרא, כי אפ דומיין מאפשרת לעבוד מרחוק.
שיטת רימוטינג, אם נתקלתם בזה, הם לא תומכים בכל השיטות האלה. ברוב המקרים אנחנו לא נצטרך את זה ולכן אם אנחנו רוצים להשתמש או בסמאפור או במנואל ריסט איבנט או ב-reader writer lock, יש לנו את גרסאות הסלימס שלהם.
הרבה יותר מהר ומומלץ לעבוד עם גרסאות אסלים. האובייקט הבא שמאפשר לבצע נעילות, אובייקט מאוד מעניין, או ליתר דיוק מחלקה מאוד מעניינת, נקראת interlocked.
בואו נפתח פה פרויקט, קונסול, נקרא לו interlocked example, ונבין במה מדובר. interlocked זאת מחלקה שיש לה סדרה של פונקציות שמאפשרות בצורה, פונקציות סטטית, כן? סטטיות, שמאפשרות לבצע נעילות, אבל רק על משתנים פרימיטיביים.
זאת אומרת, אם יש לנו כאן לדוגמה אינטנאם, נכניס לתוכו 0 ונבצע משהו מאוד פשוט כמו פלוס-פלוס. אם אנחנו רוצים לבצע פעולות על כל מיני מספרים פרימיטיביים, להוסיף מספר, להוריד מספר, להוסיף כמות של מספרים, לבצע השוואה וכתלות בהשוואה לבצע פעולה,
אז אנחנו יכולים לבצע פה לוקינג. ואז יש לנו פה את כל הקונספט של נעילות עם דגל נעילה, אם אנחנו משתמשים עם interlocked אז הנעילה מתבצעת ברמת ה-CPU עצמו, ברמת ה-registers של ה-CPU.
מעבד המחשב מכיל אזור זיכרון מאוד קטן שיש בו משתנים שהם מאוד מאוד מהירים, הם הרבה יותר מהירים ממשתנים רגילים. והמשתנים האלה נקראים registers. ומה שהמחלקה הזאת, ה-interlocked מבצעת, היא מאפשרת לבצע נעילה והיא משתמשת במשתנים האלה בשביל לבצע את הנעילות.
זאת אומרת שהיא עובדת הרבה יותר מהר מלוקינג רגיל שביצענו עד עכשיו, אבל היא טובה רק אם אנחנו רוצים לבצע נעילות עבור הפעולות של המשתנים הפרימיטיביים האלה, כמו אינטג'ר וכולי.
אבל למה אנחנו צריכים לבצע נעילה? אז כמובן שאם אנחנו מסתכלים על הפעולה הזאת, פעולה של פלוס פלוס דורשת נעילה. למה? היא נם פלוס פלוס שווה לפעולה של נם שווה לנם פלוס אחד.
יש לנו כבר כאן שתי פעולות, יש לנו הוספה של מספר והשמה, ואם בכלל היה לנו פה איזשהו ערך אחר והיינו עושים את זה, אז קודם כל היה לנו כאן את ההשמה של a שווה לנם, ורק לאחר מכן נם שווה לנם פלוס אחד.
אם היינו עושים את הפלוס פלוס כאן, אז כמובן שהייתה פה עוד פעולה ואנחנו רואים שיש כאן כל מיני מצבים שבהם המערכת עושה את הפעולות האלה. אז במקום להשתמש בנעילות רגילות אנחנו יכולים לבצע קריאה ל-interlocked.increment לתת רף של num וזאת פעולה ששווה ל-num++.
אם אנחנו עכשיו נבצע השמה של num, אנחנו אמורים לראות פה אחד, בואו נריץ את זה ונראה אחד. אבל הפעולה הזאת עשתה את זה על ידי נעילה פנימית ואם המשתנה הזה לדוגמה הוא משתנה סטטיק שמשרת מספר רב של טרדים, אנחנו יכולים להיות בטוחים שאף טרד לא נכנס בתהליך
ההעלאה ושינה את ערכו באמצע לפני שהאינקרמנט הסתיים. בואו נריץ את זה, נראה שזה עובד כמו שצריך, בסדר. בואו נעבור לפונקציה הבאה שיש לנו, interlocked.
decrement ref num, והדבר הזה מקביל למינוס מינוס. אז יש לנו פה התפסה עוד פעם של num, ואמור להיות לנו שוב פעם 0 כאן, מעולה.
בואו נעבור לפונקציה הבאה, interlocked. add ref num, ונוסיף 10. הדבר הזה מקביל ל-num+ = 10. נדפיס פה את num ואמור להיות בתוכו 10. מעולה.
יכלנו גם לבצע מינוס 10 ואז הוא היה מוסיף לנו מינוס 10. לאחר מכן יש לנו את הפונקציה exchange והדבר הזה שם ב-num 100. זאת אומרת exchange זה בעצם השמה רגילה.
בואו נריץ ונראה שערכו הופך להיות 100. אפשר אפילו לעשות כזה דבר. קודם כל שמנו בו 100 ואז העלינו אותו ב-10 ונראה שערכו יהיה 110. ואז אנחנו יכולים להיות בטוחים שפה יש לנו את ה-exchange, שזאת השמה רגילה וזו הוספה.
לאחר מכן יש לנו את interlocked.comparc exchange, שים בו 200 אם ערכו שווה 100. וזה בעצם שווה ל- אם num הוא 100, אז num שווה 200. ככה יש לנו פה כל מיני פונקציות מאוד מאוד פשוטות. ערכו לא 100, ערכו היה 110. בואו נלך ונגדיר כאן 110 ואז נשים בתוכו 200. מעולה.
יש לנו פה כל מיני פונקציות מאוד מאוד פשוטות שמבצעות פעולות פשוטות על משתנים פרימיטיביים כמו אינטג'ר, לונג.
בעצם אינטג'ר ולונג אנחנו רואים שאלה המשתנים שמאפשרים לבצע את זה. הנעילות שלהם הן הרבה יותר מהירות מלוקינג רגיל.
הדבר הבא שאנחנו הולכים לראות בעולם הטרדים לא קשור לסינכרוניזציה, אלא קשור להפרדה של מידע בין טרדים שונים. אז סגרנו את נושא הסינכרוניזציה ואנחנו הולכים לראות מה זה אומר הפרדת מידע.
ויש לנו קונספט שנקרא Thread Local Storage. בואו נסתכל מה זה אומר. טרד לוקל סטורג', אני פותח פה פרויקט חדש, קונסול אפליקיישן, ואני רוצה לעשות איזושהי הדגמה מאוד פשוטה.
בואו נניח שיש לנו פה איזשהו משתנה אינטג'רי, נקרא לו מיינמבר, בואו ניקח איזושהי פונקציה ו-Do some work 1, תיגש למשתנה הזה 100 פעם ותוסיף לו איזשהו מספר.
אחרי שהיא סיימה, היא מציגה את מיינמבר. ומה שאנחנו רוצים לעשות זה להריץ את הפונקציה הזאת על ידי סדרה של טרדים. אז בואו ניקח כאן טסט אחת, נפתח פה Thread E1, הוא מצביע על הפונקציה Do Some Work Out, יש לנו פה עוד טרד, אבל אנחנו לא רוצים שכל טרד יהרוס את המשתנה של הטרד השני,
אנחנו לא רוצים לבצע פה משהו שקשור לסינכרוניזציה, אלא אנחנו רוצים לראות שכל הטרדים עובדים על אותו משתנה. ולכן מה שנעשה זה t1.join, הטרד הראשון סיים, רק אז נתחיל את t2, וכש-t2 יסיים אז בעצם סיימנו את הפונקציה הזאת.
בואו נקרא לה מהמיין, טסט 1, וגם במיין נדפיס את מיין-נמבר. מה אנחנו הולכים לראות? או אפילו בואו נדפיס את מיין-נמבר בסיום שתי הפונקציות, זה יהיה יותר הגיוני.
קודם כל, הטרד הראשון פועל בצורה נפרדת על המספר הזה. סיים להכניס את תוכו 100 הערכים, כמובן שיוצג פה את המספר 100. הטרד השני מבצע את אותו הדבר והוא עובד על אותו משתנה עם אותה הפונקציה.
אז כמובן שהוא יציג לנו 200. ובסיום ששתי הפונקציות האלה יתבצעו על ידי שני הטרדים, אנחנו אמורים לראות את המספר 200. זאת אומרת, מה שאנחנו הולכים לראות זה 100, 200, 200. בואו נריץ ונראה שיש לנו 100, 200, 200.
אז מה ואומר טרד לוקל סטורג'? יש לנו אפשרות לקחת סטורג', כלומר מידע, ולגרום לו להיות שונה מבחינת אזור זיכרון בין טרדים שונים.
אם אנחנו רוצים שטרד יעבוד בנפרד על אזור זיכרון שהוא שונה מאזור זיכרון אחר, יש לנו פה את הקונספט של פרד לוקל סטורג'. סטורג' זה אזור זיכרון, הוא לוקאלי לגבי טרד.
זה קונספט הפוך לגמרי מהקונספט הקודם שאמר שאנחנו רוצים משתנה אחד או משאב אחד ולבצע סינכרוניזציה בין כל הטרדים כך שטרד שעובד על המשאב הוא חוסם טרדים אחרים. פה יכול להיות שאנחנו אומרים יש לנו לוגיקה בכלל אחרת, זה רק איזשהו משתנה שאנחנו רוצים שהוא ישרת טרד ספציפי ויהיה שונה בין טרד אחד לטרד אחר, הוא לא משותף בין כל הטרדים.
יש לנו כל מיני אפשרויות לעשות את זה. אפשרות אחת זה פשוט משתנה או ליתר דיוק אטריביוט כונה סטטית מעל למשתנה הסטטי אטריביוט שנקרא thread static.
ברגע שרשמנו את הדבר הזה גרמנו למשתנה הזה להיות שונה עבור כל טרד. זאת אומרת שהמשתנה הזה ייפתח בנפרד עבור כל טרד. אז מה אנחנו נראה עכשיו אם נריץ את זה?
הטרד הראשון עובד על משתנה נפרד ולכן ערכו