Thread safe List

LiranViper

New member
Thread safe List

היי,

למרות שזה נראה בסיסי בצורה מוגזמת, יש משהו שאני אשמח להתייחסות אודותיו..

יש לי List של אובייקטים, שאני עובר עליה בParallel.ForEach.
בתור הלולאה לעיתים מList נמחק/נוסף אובייקט.
בנוסף, יש ת'רד שממנו יכול להתווסף אובייקט (לא רלוונטי יותר מדי כי כבר קורה בממילא במשפט הקודם שכתבתי).


בגלל שאני צריך אפשרות למחוק, אני לא יכול להשתמש בConcurrencyBag ונשאר לי פחות או יותר לממש לבד Thread safe List..

עולות לי כמה שאלות:
1. מהשיטוטים שלי ברשת, הצלחתי למצוא רק מימושים שעושים שימוש בlock. השאלה היא, למה אין שימוש במנגנונים יותר יעילים כמו ReaderWriterSlimLock?

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

3. אם סעיף 2 לא תרם לי ואני מממש בעצמי, אז האם לשים ReaderWriterSlimLock על wrapper ל- Add,Remover,Get property זה מספיק טוב? אני שואל בעיקר בגלל הGet Property, כי חשבתי על מצב שבו מישהו ניגש דרך הGet לList כדי בקש את הרשימה, והמנגנון שלי דואג שכשהוא מקבל את הרשימה הוא אכן במצבThreadSafe, אבל האם הפנייה לאחר מכן היא כבר לא ThreadSafe? (למשל, פנייה לGet מביאה את הרשימה, והפנייה אח"כ ל- לFirst(..) היא כבר לא בטוחה). כלומר האם אני בעצם להכמיס את הגישה לרשימה עצמה, ולעטוף בנעילה בעצמי גם את כל המתודות First, Exists, Where וכו'

תודה, לירן
 

Royi Namir

New member
מממ

למה לא להשתמש ב ConcurrentDictionary ? ( עזוב את ה KEY עכשיו - זה לא משנה - שיהיה משהוא יחודי)
 

LiranViper

New member
אופס לא שמתי לב שכתבת Dictionary

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

האם זה best practice להשתמש ככה במילון כשצריך רשימה עם מחיקה? מבחינת ביצועים וכו'

בכל אופן, אני אשמח אם יענו לי על השאלות ששאלתי לצורך למידה.

ותודה רבה!
 

ראסטלין

New member
אני הייתי מבצע אחרת...

Single responsibility!!!
הייתי יוצר רשימה והייתי יוצר מנגנון נפרד שרץ על הרשימה.
ז"א לא לרוץ ב- ForEach, אלא להוציא את ה- Counter מהרשימה, ואז מריץ מעין פויטר שקופץ למיקום הנכון.
כך יש מנגנון שמוסיף ומוריד איברים מהרשימה ויש מנגנון שעובר על האיברים ופועל עליהם לפי הצורך
 

LiranViper

New member
הבעיה לא בForEach

הבעיה לא בForEach, או בשינוי רשימה בזמן שרצים עליה (ולמעשה Paralle.Foreach פותר את זה.. מותר לשנות את הרשימה מתוך Parallel.ForEach. וגם אם לא היה אפשר, הייתי יכול לרוץ בFOR רגיל ולגשת לפי אינדקס כמו שהצעת).

הבעיה היא בכך שיש לי ת'רדים שונים שמוחקים/מוסיפים לList, ועל כן אני צריך שהרשימה תהיה Thread safe
 

ראסטלין

New member
אפרט קצת יותר את החלק הרלוונטי

המנגנון של ההוספה וההסרה.
סתם רעיון של חצות, אז תיקח בערבון מוגבל ( אני מת מעייפות )
תיצור מחסנית, נקרא לה "מחסנית פעולות לביצוע על ה - List".
כל Thread שרוצה לבצע פעולה, מכניס למחסנית בקשת פעולה ( לא משנה מה, הוספה\הסרה ).
ואז יהיה חלק שעושה Pop ל-Request-ים ומבצע אותם באופן סינכרוני ובקצב שלו.

לילה טוב
 

Royi Namir

New member
ממש לא

הבחור רוצה לעבוד MULTITHREAD עם פקד שתומך ב MULTI ואתה נותן לו פקד אחד שלא תומך ?

יש הבדל בין לממש LIST או תור יחד עם LOCK לעומת CONCURRENT XXX

CONCURRENT BAG מממש בתוכו כמה LIST כך שהנעילה היא לא על הכל אלא על LIST מסויים שבו נמצא ה אובייקט

הוצאת לו את כל ה PARALLISM מהשאלה.
 

Royi Namir

New member
?

לא הבנתי , למה אתה מתכוון ?
לפי השאלה שלו הוא רוצה דרך כמה THREAS להכניס ל BAG

תור לא יתן לו את היעילות של מקביליות
 
"יעילים יותר"? האם בדקת?

ואם אתה מתעניין ביעילות, למה בכלל להשתמש ב Parallel.ForEach לתרחיש הכולל הסרת/הוספת איברים והרשימה? האם זה "יעיל"? האם בדקת?
מדוע אינך עובד עם concurrentQueue ?
 
באופן אישי הייתי נמנע בכל מחיר מהתרחיש הזה

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

LiranViper

New member
אוקי- קצת יותר פירוט על מה אני עושה

קודם כל תודה לכולכם.

אז ככה,
יש לי List של Member
וList של Action

לפני שאני אמשיך, כדאי אולי לציין שגם לMember וגם לAction יש מזהה חח"ע, ולכן באמת אולי היה יותר מתאים להשתמש בDictionary כפי שרועי הציע.

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

העניין השני שיש להתחשב בו, הוא שבכל שלב יכול מת'רד אחר להתווסף Member חדש לרשימת Member-ים.
 
אז תשכפל את רשימת ה member לפני שאתה מתחיל

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

LiranViper

New member
תגובה

חשבתי על זה, אבל Members יכול להיות גדול מאוד, לכן אני לא בטוח שזה עדיף על נעילה (תקורה של מעבר על כולם)

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

ולגבי הAction-ים, שם אני אשתמש ב concurrencyDictionary?
 

LiranViper

New member
בעצם למה בכלל לשכפך Members?

אם אני עובר על Members עם Paralle.ForEach, בכלל אין מניעה שMembers תשתנה תוך כדי שעוברים עליה.. (בניגוד למעבר עם foreach קלאסי)

לגבי Actions- השאלה נותרת בעינה
 
למעלה