נובמבר סנכרון תהליכים וחוטים Process and Thread Synchronization חלק שני
נובמבר עמוד נושאים מה למדנו בשיעור הקודם? מנגנוני תיאום ברמה גבוהה –סמפורים –מוניטורים דוגמא –Windows NT בעית הפילוסופים הסועדים
נובמבר עמוד מה למדנו בשיעור הקודם? למה דרוש תיאום? –גישה בו זמנית, לא מוגנת למשתנים משותפים ניסינו להסתדר בלי עזרת החומרה –אלגוריתם "קופת חולים" לא יעבוד בסביבות out-of-order execution פקודות חומרה לתיאום –test&set, comp&swap, load-linked – store-conditional
נובמבר עמוד סמפורים - הגדרה צמד פקודות אטומיות על משתנה משותף מסוג semaphore הכולל מונה ותור וריאנטים –סמפור בינרי (mutex) מקבל רק ערכים 0, 1 –סמפור מונה מקבל כל ערך לא-שלילי מייצג משאב עם מספר עותקים זהים p(s): while (s=0) do wait(s) s:=s-1 v(s): s:=s+1 signal(s)
נובמבר עמוד דוגמא – בעית יצרן/צרכן שימוש בשני סמפורים מונים –הסמפור bufferSpaceAvail מסמן את מספר המקומות הפנויים בחוצץ (מאותחל ב-n) –הסמפור itemsAvailable מסמן את מספר העצמים המוכנים בחוצץ (מאותחל ב-0) Producer: repeat p(bufferSpaceAvail) buff[pp]:=new item pp:=(pp+1) mod n v(itemsAvailable) until false Consumer: repeat p(itemsAvailable) consume buff[cp] cp:=(cp+1) mod n v(bufferSpaceAvail) until false
נובמבר עמוד דוגמא – בעית יצרן/צרכן הערות חוסר מבניות –החוט ה-"נועל" סמפור אינו אותו חוט ש-"משחרר" אותו
נובמבר עמוד דוגמא – בעית קוראים/כותבים הגדרה –שני סוגי חוטים: קוראים וכותבים –מותר למספר חוטים קוראים לקרוא בו-זמנית –אך כאשר חוט כותב, אסור שחוטים אחרים יכתבו ו/או יקראו מניעה הדדית ReaderWriter Reader Writer
נובמבר עמוד דוגמא – בעית קוראים/כותבים שימוש בשני סמפורים בינריים –sread – מגן על מונה מספר הקוראים אך יש לו תפקיד נוסף... –wsem – מניעה הדדית בין קוראים לבין כותבים Reader: p(sread) r:=r+1 if r=1 then p(wsem) v(sread) Read p(sread) r:=r-1 if r=0 then v(wsem) v(sread) Writer: p(wsem) Write v(wsem)
נובמבר עמוד דוגמא – בעית קוראים/כותבים הערות שימוש יותר מובנה בסמפורים –החוט שנועל את sread הוא אותו חוט שמשחרר אותו –האם זה גם נכון לגבי wsem? הוגנות (או חוסר הוגנות) –בהנחה שמימוש הסמפורים הוגן, האם הדוגמא מבטיחה הוגנות?
נובמבר עמוד דוגמא – גישה למאגר חוצצים מאגר חוצצים לתקשורת בין חוטים לטרמינלים משתנים –fullBuffers – מספר החוצצים המלאים (סמפור מונה) –freeBuffers – מספר החוצצים הפנויים (סמפור מונה) –c[1..n] – האם החוצץ נעול למידע חדש (סמפורים בינריים) –buffer[1..n] – החוצצים המכילים את המידע handled – דגל בינרי לכל חוצץ המציין האם המידע שבו שודר buffer 0 buffer 1 buffer n-1 sm pw sm
נובמבר עמוד דוגמא – גישה למאגר חוצצים cp(sem) – דומה ל-p(sem) אך לא חוסם –אם sem=1 אזי מציב 0 ומחזיר true –אחרת, מחזיר false buff[j].handled לא מוגן – האם זה מהווה בעיה? Thread PW: j:=0 repeat p(fullBuffers) while(buff[j].handled) do j:=(j+1) mod n transmit buff[j] buff[j].handled:=1 v(c[j]) v(freeBuffers) until false Thread SM: j:=0 repeat p(freeBuffers) while not cp(c[j]) j:=(j+1) mod n prepare buff[j] buff[j].handled:=0 v(fullBuffers) until false
נובמבר עמוד מוניטורים סמפורים אינם מספקים אבסטרקציה טובה –קשר בין המנעול לבין המבנה המוגן על ידו אינו ברור מאליו –דורש שכולם ישחקו לפי הכללים מה קורה אם שוכחים פקודת p(sem) או v(sem)? מוניטור –מבנה נתונים אבסטרקטי (הכולל נתונים ופעולות לגישה אליהם) מבטיחים מניעה הדדית בביצוע פעולות על המבנה מספקים משתני תנאי (condition variables) להמתנה
נובמבר עמוד מוניטורים תורים למשתני תנאי מצב המוניטור Procedure 1 Procedure 2 Procedure N Initializer x y תור גישה למוניטור
נובמבר עמוד מוניטורים – פעולות על משתנה תנאי wait(condvar) –החוט המבצע "משחרר" את המוניטור ונכנס להמתנה בתור של משתנה התנאי signal(condvar) –מעירים את אחד הממתינים בתור של משתנה התנאי מי ממשיך לבצע? החוט המבצע signal? החוט המוער? שניהם? –מה קורה כאשר אין חוטים שממתינים על משתנה התנאי? פעולה חסרת זיכרון broadcast(condvar) –מעירים את כל הממתינים בתור של משתנה התנאי
נובמבר עמוד דוגמא – מימוש מחסנית AddToStack(item): put item on stack signal(notEmpty) RemoveFromStack(item): if stack empty then wait(notEmpty) remove item from stack
נובמבר עמוד מוניטורים וריאנטים נעילה מפורשת בכניסה –בתפיסה הטהורה – המתכנת לא נדרש לנעול את המוניטור –במימושים מסויימים (Solaris, Windows NT, …) צריך לתפוס את המנעול בצורה מפורשת מי ממשיך להתבצע לאחר signal? –לפי גרסת Hoare, שולח הסיגנל נכנס להמתנה עד שהמוניטור משתחרר (בתור הכניסה – לא על משתנה תנאי) –לפי גרסת Mesa, שולח הסיגנל ממשיך להתבצע חייבים לבדוק את התנאי מחדש
נובמבר עמוד דוגמא – מימוש מחסנית מימוש עם נעילה מפורשת,וסמנטיקת סיגנל לפי Mesa AddToStack(item): lock_acquire() put item on stack signal(notEmpty) lock_release() RemoveFromStack(item): lock_acquire() while stack empty then wait(notEmpty) remove item from stack lock_release()
נובמבר עמוד דוגמא: קוראים / כותבים int ar; // Active readers int aw; // Active writers int wr; // Waiting to read int ww; // Waiting to write Cond okToRead, okToWrite;
נובמבר עמוד דוגמא: קוראים / כותבים ReaderEntry: lock_acquire() while ((aw+ww)>0)} wr++ wait(okToRead) wr-- { ar++ broadcast(okToRead) lock_release() WriterEntry: lock_acquire() while ((aw+ar)>0)} ww++ wait(okToWrite) ww--} aw++ lock_release() ReaderExit: lock_acquire() ar–- if (ar==0)&&(ww>0) signal(okToWrite) lock_release() WriterExit: lock_acquire() aw–- if (ww>0) signal(okToWrite) else if (wr>0) broadcast(okToRead) lock_release()
נובמבר עמוד איזו פקודה חזקה יותר? סמפורים לעומת מוניטורים מימוש מוניטור באמצעות סמפור –ניסיון 1: wait(c) p(s) signal(c) v(s) –יגרום לקיפאון בין מנעול כניסה למוניטור לבין משתני תנאי mon_entry p(mutex) mon_exit v(mutex)
נובמבר עמוד איזו פקודה חזקה יותר? סמפורים לעומת מוניטורים מימוש מוניטור באמצעות סמפור –ניסיון 2: –בעיות לסמפור יש "זיכרון" מימוש אפשרי מתואר ב-Silberschatz&Galvin האם ניתן לממש סמפור בעזרת מוניטור? wait(cond): lock_release() p(cond) lock_acquire() signal(cond): v(cond)
נובמבר עמוד מנגנוני תיאום ב-Windows NT כל אלמנט במערכת ההפעלה הוא אוביקט –על כל אוביקט ניתן להמתין ו/או לשחרר אוביקטים מיוחדים –mutex– מנעול הוגן –event – משתנה תנאי (עם אפשרות ל-broadcast כדי להעיר את כל הממתינים) –semaphore – סמפור מונה, לא הוגן –critical section – light-weight mutex המיועד לחוטים באותו תהליך
נובמבר עמוד דוגמא – הפילוסופים הסועדים כל פילוסוף מבלה ב- –חשיבה (thinking) –ניסיון להשיג מזלגות (hungry) –אוכל (eating) Every philosopher i: repeat pickup(i) eat putdown(i) until forever
נובמבר עמוד הפילוסופים הסועדים פיתרון בעזרת מוניטורים monitor diningPhilosopher array state[0..N] of {thinking, hungry, eating} array self[0..N] of condition pickup(i) state[i]:=hungry test(i) if state[i]!=eating then wait(self[i]) putdown(i) state[i]:=thinking test((i-1)mod N) test((i+1)mod N) test(k) if (state[(k-1)mod N]!=eating and state[(k+1)mod N]!=eating and state[k]=hungry) then { state[k]:=eating signal(self[k]) }
נובמבר עמוד הפילוסופים הסועדים פיתרון בעזרת מוניטורים חיסרון –חוסר מקביליות בין פילוסופים לא קשורים כל כניסה למוניטור נועל את מבנה הנתונים כולו! פיתרון נאיבי בעזרת סמפורים –נקצה סמפור fork[i] לכל מזלג –p(fork[i-1]); p(fork[i]); eat; v(fork[i-1]); v(fork[i]); –בעיה: קיפאון!