פייתון: הריגת thread כשמאבדים גישה אליו - תכנות - HWzone פורומים
עבור לתוכן
  • צור חשבון

פייתון: הריגת thread כשמאבדים גישה אליו


SinMaker

Recommended Posts

היי,

שאלתי:

יש לי אובייקט שבאתחול שלו מפעיל מספר threadים.

מהי הדרך הפשוטה ביותר להרוג אותם בעת איבוד הרפרנס לאובייקט (כשלא ניתן יותר לגשת לאובייקט מבחוץ)?

רבים מכם כמובן יגידו __del__, אך הדבר אינו אפשרי במקרה זה, שכן יש רפרנסיים מעגליים.

הצעות?

קישור לתוכן
שתף באתרים אחרים

לדאוג לממש try,except,finally עם פונקציית cleanup מתאימה.

לחילופין להרשם בכל יצירת אובייקט לclass סטטי שינקה בסוף הריצה או בעת הקפצת "event" מתאים עם __del__.

קישור לתוכן
שתף באתרים אחרים

try,except,finally על מה? ה-threadים אמורים להמשיך לרוץ כל עוד יש גישה לאובייקט מאיזשהו scope.

מהי כוונתך ב-"להירשם ל-class סטטי שינקה בסוף"? מתי מגיע הסוף? אני לא יכול לדעת.

__del__ גם אינו אופציה שכן הוא אינו נקרא כי יש רפרנסיים מעגליים

קישור לתוכן
שתף באתרים אחרים

אין שום דבר שמונע ממך לקרוא ל-__del__ בעצמך עם del statement:


clean_me_up = YourClass()
# do stuff
del clean_me_up

(או לחילופין לתת לו מתודה close)

וזה בסדר גמור. הבעיה היחידה זה שקל לשכוח את ה-del אם אתה יוצר אותו בכל מיני מקומות שונים.

ולכן כדאי להשתמש ב-context managers (with statement)s, תקרא על זה. זה פותח לך scope שבו אתה משתמש באובייקט ובסופו הוא נסגר לפי המתודה __exit__.

עריכה: אם אתה לא יודע מתי הוא צריך להסתיים ולא רוצה לחכות להט'רדים שייסתימו אז אתה יכול להוסיף למחלקה את מספר הט'רדים שלה ובסוף כל אחד מהם לבדוק אם הוא הט'רד האחרון ואם כן שיקרא ל-del. אני לא בטוח אם זה הכרחי כי לא נתקלתי במצב כזה אף פעם... למה אתה צריך רפרנסים מעגליים? עדיף להמנע מהם אם אפשר.

עריכה2: מבדיקה מעמיקה יותר, תבדוק את weakref, http://eli.thegreenplace.net/2009/06/12/safely-using-destructors-in-python/

קישור לתוכן
שתף באתרים אחרים


del x

שקול ללעשות:


x = None

ושניהם חסרי משמעות במקרה הזה כי יש רפרנסיים מעגליים. ה-GC לא יפנה את האובייקט.

context managers לא פותר את הבעיה כי אני לא יכול לכפות אותו על מי שמשתמש ב-API שלי.

התרדים לא אמורים להסתיים, הם צריכים לרוץ כל עוד האובייקט קיים ואפשר לגשת אליו מבחוץ.

האובייקט שמכיל את התרדים הוא אובייקט עצום! קשה מאוד לבנות אובייקט בסדר גודל כזה ללא מעגלים. מספיק שיהיה מעגל אחד (ו-__del__ אחד) כדי שה-GC לא יידע כיצד להתמודד עם הפינוי שלו.

נ.ב: תודה על הניסיון לעזור אבל מי שכתב את המאמר בלינק הוא אידיוט או שלעולם לא עבד בתעשייה.

קישור לתוכן
שתף באתרים אחרים

לגרום לתרדים שאובייקט יוצר בעת האתחול שלו למות כאשר אין גישה לאובייקט.

לדוגמא, אם האובייקט היה נבנה באופן זהה, רק ללא התרדים, בעת איבוד המצביע האחרון אליו הוא היה משתחרר ע"י python במקרה הטוב וע"י ה-GC במקרה הפחות טוב.

קישור לתוכן
שתף באתרים אחרים

אגב אפשר לשאול מה ההיגיון לאתחל threadים מתי שאתה מייצר instance של אובייקט?

בכלליות זה נשמע רע.

בכ"מ בכל מקום בפייתון שיש בעיה כזאת יוצרים מתודת close מתאימה, שמאפשרת שימוש בwith, אם הצרכן שלך טיפש- שיסבול.

קישור לתוכן
שתף באתרים אחרים

האובייקט הוא אובייקט די מורכב שמתקשר עם interpreters של python שרצים על מחשבים אחרים ברשת, הוא מעין server-client עם תורים של בקשות ותשובות.

"אם הצרכן שלך טיפש- שיסבול" - זה לא משפט שעובד כל כך טוב בתוכנה.

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

קישור לתוכן
שתף באתרים אחרים

היי,

קודם כל לגבי reference counting בפייתון. זה לא משהו שהשפה מגדירה, זה תלוי ב interpreter עצמו.

עד כמה שאני יודע cpython הוא היחיד שעושה RC, גם iron וגם jython לא עושים.

ולגבי cpython. יש לך כבר כמה שנים טובות garbage collector שנועד בדיוק למצבים כאלה. קבל:


>>> import gc
>>> gc.collect()
0
>>> for i in range(100):
... x = []
... x.append(x) # create cycle for each new list object
...
>>> gc.collect() # cleanup
100
>>>

קישור לתוכן
שתף באתרים אחרים

בדיוק למצבים כאלה:

במקרה זה, שכן יש רפרנסיים מעגליים.

ואם אתה מכיר, אז למה לבזבז לאנשים ת'זמן?

ורק בשביל להיות ברור יותר.כשאני אומר GC, אני לא מתכוון ל reference counting .

קישור לתוכן
שתף באתרים אחרים

בטח שהוא ימשיך לרוץ! מה ציפית? הוא ירוץ עד ש (א) התהילך יסתיים או (ב) run תחזור, ואז הוא יתנקה כמו כל אובייקט.

לא בטוח שהבנתי מה הקשר ל GC ומעגלים? הנה קוד קצר להמחיש את זה:


import threading
import gc

gc.collect()
for i in range(10):
x = threading.Thread(target=None)
x.x = x # create ref cycle
x.start()

while threading.active_count() > 1:
continue

print "GC :", gc.collect()

אם תעיף את את השורה שעושה מעגל, האובייקט ימות דרך ה ref count .

אם יש לך אובייקט שמכיל בתוכו thread פשוט תתדאג לשלוח לתרד הודאה שהגיע הזמן לחזור... זה הכל.

אה ואם כבר תרדים, אני מציע שתשקול לממש thread pool נורמאלי, כי אחרי הכל אלה עדין posix threads מתחת למכסה,

ותאמין לי אתה לא רוצה 849 מהם רצים ברקע :)

אני גם מקווה שאתה מודע למגבלה שיש על תרדים ב cpython .

אם לא, תתחיל לקרוא פה. אה ואם אתה מרגיש מוטרד, תקרא גם על stackless python.

בהצלחה :)

קישור לתוכן
שתף באתרים אחרים

ארכיון

דיון זה הועבר לארכיון ולא ניתן להוסיף בו תגובות חדשות.

×
  • צור חדש...