דילמת נעילת Singleton

בטיטi

New member
דילמת נעילת Singleton

נניח ויש לי את הסינגלטון הבא :
public class JobsMAnager { private static JobsMAnager m_Current; public static JobsManager Current { get { if (m_Current == null) { m_Current = new StateManager(); } return m_Current; } } }​
יש כאן בעיה, במידה ו 2 משתמשים יגשו בו זמנית בדיוק, יווצרו 2 מופעים של האובייקט. ניתן להוסיף Lock מעל התנאי, אבל אז פשוט העברנו את אותה הבעיה שורה אחת מעלה. יצא לי לראות קוד שמשתמש ב Lock מקונן, אבל גם שם לדעתי קיימת אותה בעיה בדיוק. כיצד מתמודדים ? (אולי ממתינים פרק זמן רנדומלי לפני נעילת הקטע הקריטי ?)
 

Justin Angel

New member
קה פרובלמה מואי צ'יקיטה?

אני אישית מעדיף להימנע מאתחול Lazy של סינגלטונים. זה רק יוצר בעיות. אתחול פשוט וסטטי עדיף בהרבה על אתחול מסובך וקשה:
public sealed class Singleton { static readonly Singleton instance=new Singleton(); // Explicit static constructor to tell C# compiler // not to mark type as beforefieldinit static Singleton() { } Singleton() { } public static Singleton Instance { get { return instance; } } }​
האתחול הזה הוא גם Lazy (היות ורק בגישה הראשונה למחלקה Singleton יתאתחל המשתנה הסטטי הפנימי) אבל בצורה שקטה והגיונית יותר.
 

בטיטi

New member
תגובה :

1. מאופן כללי, מהו אתחול Lazy ? 2. מה יקרה אם בפעם הראשונה 2 יאתחלו את המחלקה בדיוק באותו הרגע ? 3. מה בדיוק הקטע של ה static constructor ?
 

בטיטi

New member
תודה, עדיין לא הבנתי נושא אחד :

אם מסתכלים על ההבדלים בין הגירסא הראשונה לשניה (בקישור), אני לא מבין מדוע בדוגמא השניה אין בעיה של כפילות, הרי יתכן ששני ה Threads יגיעו לשורת הנעילה בדיוק באותה מילי שניה.
 

Misprint

New member
למרות שיש לך כמה thread'ים

המעבד שלך יכול לבצע רק פעולה אחת בזמן נתון, כלומר, למרות שעם עבודה ב multithreading אתה מקבל אשליה שדברים קורים "בו זמנית", הם למעשה לא..יתכן ואפשר להכנס לכל מיני מקרים ספציפים של ארכיטקטורות מעבדים שיכולות לעשות דבר שכזה, אבל אני יוצא מנקודת הנחה שזה לא המצב כרגעבכל אופן, ה lock שלך לא יכול להקרא "בו זמנית", ולכן.. ה lock יתפוס..שים לב שמסופקים לך בפריימוורק עוד כל מיני כלים נחמדים לעבודה עם lock'ים, אם זה מעניין אותך, הייתי ממלית לקפוץ לכאן: http://www.albahari.com/threading/
 

בטיטi

New member
אז רגע...

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

Misprint

New member
לא אותו הדבר..

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

Misprint

New member
בפשטות

1. בגלל שאין lock שני ת'רדים יכולים לגשת לאובייקט 2. ת'רד 1 רוצה לקרוא את הנתונים של האובייקט ולכתוב אותם לקובץ. למשל בשביל לשמור את המצב הנוכחי שלו בנקודת הזמן הנתונה. 3. ת'רד 2 רוצה לעדכן את האובייקט המשותף בנתונים חדשים 4. שני הת'רדים רצים במקביל 5. בזמן שת'רד 1 כותב את הנתונים, ת'רד 2 משנה לאובייקט את הנתונים. מה שיגרום לתוצאה של הפעולה של ת'רד 1, להיות לא נכונה 6. בשביל למנוע את זה, אפשר לשים lock, ואז כשת'רד 1 יקרא את הנתונים, אף אחד אחר לא יוכל לשנות אותם. מקווה שעזר
 

dino_din

New member
צריך את הנעילה כדי למנוע היווצרות

של שני מופעי אובייקט. השורה הבעייתית היא: if (m_Current == null) מאחר ושני ה threads רצים במקביל - יכול להיווצר מצב שה thread הראשון הגיע לתנאי ומאחר והאובייקט שווה null הוא יכנס לתנאי. עכשיו הוא מצביע על השורה הזו: m_Current = new StateManager(); אבל לפני שהוא מבצע אותה הוא מסיים את זמנו וזמן הריצה עובר ל thread השני. עכשיו ה theard השני מגיע לתנאי ורואה שהאובייקט עדיין null (כי ה thread הראשון עוד לא ביצע את ההשמה) ולכן גם הוא נכנס לתנאי וגם הוא מצביע על השורה הזו: m_Current = new StateManager(); במצב כזה יש לנו שני threads ששניהם מצביעים על השורה הזו: m_Current = new StateManager(); וכאן הבעיה. בריצה הבאה שניהם ינסו ליצור מופע של StateManager.
 
למעלה