10.4 מודל הזכרון תרגול
תרגול - מודל הזכרון - memory model and volatile¶
תרגול 1 - volatile ואופטימיזציות¶
- כתבו את הקוד הבא ללא volatile:
- ב-thread אחר, הוסיפו
sleep(1)ואזflag = 1. - קמפלו עם
-O2והריצו. מה קורה? (רמז: ייתכן שהתוכנית תתקע) - הוסיפו
volatileלמשתנה flag וקמפלו שוב. מה קורה עכשיו? - הסבירו את ההבדל: מה הקומפיילר עשה עם האופטימיזציה?
תרגול 2 - מונה אטומי¶
- כתבו תוכנית עם מונה גלובלי
int counter = 0. - צרו 4 threads, כל אחד מגדיל את המונה 1,000,000 פעמים (
counter++). - הדפיסו את הערך הסופי. האם הוא 4,000,000? הריצו כמה פעמים ובדקו.
- החליפו את
int counterב-atomic_int counterושנו אתcounter++ל-atomic_fetch_add(&counter, 1, memory_order_relaxed). - הריצו שוב. האם עכשיו התוצאה תמיד 4,000,000?
- הסבירו למה
memory_order_relaxedמספיק כאן (רמז: אנחנו לא מסתמכים על סדר מול משתנים אחרים).
תרגול 3 - producer/consumer עם acquire/release¶
- ממשו דפוס producer/consumer:
- משתנה גלובלי
int data. - משתנה אטומי
atomic_int ready = 0. - thread היצרן: מכין את data (שם ערך), ואז עושה
atomic_storeעםmemory_order_releaseל-ready. - thread הצרכן: ממתין בלולאה עד ש-ready הוא 1 (עם
atomic_loadו-memory_order_acquire), ואז קורא את data. - ודאו שהצרכן תמיד קורא את הערך הנכון.
- נסו להחליף את acquire/release ב-
memory_order_relaxed. האם עדיין מובטח שזה יעבוד? הסבירו (תיאורטית, גם אם בx86 זה "עובד"). - שנו את הדוגמה כך שהיצרן שולח 10 ערכים, אחד אחרי השני. הצרכן קורא את כולם ומדפיס.
תרגול 4 - ניסוי מחסום קומפיילר¶
- כתבו את הקוד הבא:
- קמפלו עם
gcc -O2 -Sכדי לראות את האסמבלי שנוצר. בדקו את סדר הכתיבות. - הוסיפו מחסום קומפיילר בין שתי הכתיבות:
- קמפלו שוב עם
-O2 -Sובדקו את האסמבלי. האם הסדר נשמר? - הסבירו מה מחסום הקומפיילר עושה ולמה הוא לא מספיק לסינכרון בין threads (בארכיטקטורות חלשות כמו ARM).
תרגול 5 - תור lock-free¶
- ממשו תור SPSC (single-producer single-consumer) פשוט עם מערך מעגלי:
- הגדירו struct עם מערך
int buffer[64], ו-atomic_int head,atomic_int tail. - ממשו
int queue_push(queue_t *q, int val)- מחזיר 0 בהצלחה, -1 אם מלא. - ממשו
int queue_pop(queue_t *q, int *val)- מחזיר 0 בהצלחה, -1 אם ריק. - השתמשו ב-acquire/release כדי להבטיח סדר.
- צרו thread יצרן שדוחף מספרים 0-999 לתור, ו-thread צרכן שקורא אותם ומדפיס.
- ודאו שכל המספרים מגיעים בסדר הנכון וללא אבדן.
- מה קורה אם התור מתמלא? איך הthread היצרן צריך להתנהג?