שחרור משאבים ברובי ו Go

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

ברובי, יש את היכולת הזו כאשר אני משתמש במתודה, case when ואפילו תפיסת חריגות. זה נקרא בשם "ensure".
בגו הפקודה היא defer.
שניהם מבצעים קוד גם אם היתה עכשיו שגיאה שלא תפסנו אותה, ומוודאים כי החלק שנמצא שם יתבצע.
אך באותה מידה, הוא יתבצע גם אם נצא ממתודה למשל.

כיצד זה מגיע לידי ביטוי ?
הדגמה בGo:

אחרי do something here אין קוד לסגור את הקובץ, בזכות defer שהיה אחרי בדיקת שגיאות.
הוא גם מסוגל לקבל פונקציה אנונימית וניתן לבצע שם עוד בדיקות במידת הצורך.

ברובי קוד שכזה יראה כך:


def read_file
  f = open('/tmp/a_file')
  # do something here
rescue => e # catch exceptions globally
   $stderr.puts("Unable to open a_file") if e.kind_of? Errno::ENOENT
ensure
  f.close if f
end

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

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

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

3 מחשבות על “שחרור משאבים ברובי ו Go

  1. מאיר

    ל־defer יש יתרון אחד שאין בדוגמאות (כולל אלו שהעברנו בינינו בפרטי), הקרבה של שחרור המשאב ליצירתו, מה שמקל על קריאות הקוד והבנתו.

    אם ניקח את דוגמת הרובי (או try/finally בפייתון וכו'), בקשת השחרור מופיעה בסוף (אחרי כל ה־do something here), כך שצריך לסרוק למטה לוודא שאכן מתבצע.

    ב־go בקשת השחרור יכולה להופיע מיד לאחר קבלת המשאב בהצלחה.

  2. Shai

    אם כבר מדברים על הנושא, ראוי להשוות את הגישה של go ‏(כמו שמאיר רמז, הגישה של ruby שהצגת כאן לא שונה מהותית מ־try..finally המוכר משפות רבות אחרות) עם הגישות שמחברות את הקוד שמשחרר משאבים עם הקוד שמקצה את אותם משאבים בתוך class שמגדיר את הממשק למשאב, כאשר השימוש במשאב נעשה בנפרד — אם זה RAII הישן של C++‎ (השחרור נעשה ב־destructor שמובטח שייקרא כאשר משתנה לוקאלי יוצא מה־scope)‏ או הגרסה המודרנית יותר, using של C#‎ או with של Python.

    למשל, המקבילה הפיתונית לדוגמה לעיל היא:


    def read_file():‎
    with open('/tmp/a_file') as f:‎
    ‎#do something

    הפקודה with מבטיחה שביציאה מהבלוק (תקינה או עם שגיאה) תקרא הפונקציה f.__exit__()‎; האובייקט שמוחזר ע״י open מוגדר (מראש, built-in בשפה) עם מתודה כזו שסוגרת את הקובץ.

    לטעמי, הסגנון הזה יוצר הפרדה נכונה יותר בין ה־what ל־how – מבחינת הקוד המשתמש במשאב, הוא רק אומר „זה משאב שצריך לשחרר”. המחיר (כלומר, המצב בו נעדיף קוד כמו זה שמובא בפוסט) הוא במקרים שבהם המשאב הוא ייחודי, וקוד השחרור קצר ופשוט מספיק כדי שנרצה לכלול אותו בפונקציה; אבל זה, בדרך כלל, המקרה הפחות נפוץ. אני לא מכיר את go מספיק בשביל לדעת אם היא תומכת במבנה בסגנון with או using; אם defer הוא התחליף לזה, אז זו בעיניי החלטה מפוקפקת.

    1. ik_5 מאת

      אני לא מכיר את ++C כמעט בכלל (למזלי), אז לא יודע להגיב לך לגבי הטיעונים האלו, אבל העניין הוא שברובי אתה יכול לעבוד עם בלוקים, בסגנון הזהה לפיתון (בדוגמה שלך):
      open('/tmp/a_file') do |f|
      end

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

      העניין הוא ש ensure ו finally מעט שונים. למשל בג'אווה או בפסקל, אתה צריך בלוקים נפרדים שלהם בשביל שיתנהגו כמו שצריך. אפילו ברובי, אני יכול ליצור בלוג של begin rescue ולשים גם else ו ensure וליצור משהו שיודע להתמודד עם מצבים שונים.

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

כתיבת תגובה

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

הלוגו של WordPress.com

אתה מגיב באמצעות חשבון WordPress.com שלך. לצאת מהמערכת / לשנות )

תמונת Twitter

אתה מגיב באמצעות חשבון Twitter שלך. לצאת מהמערכת / לשנות )

תמונת Facebook

אתה מגיב באמצעות חשבון Facebook שלך. לצאת מהמערכת / לשנות )

תמונת גוגל פלוס

אתה מגיב באמצעות חשבון Google+ שלך. לצאת מהמערכת / לשנות )

מתחבר ל-%s