lock#
מציב מנעול מייעץ על משתנה משותף, מערך, האש או תת־שגרה עד שהמנעול יוצא מהתחום.
lock הוא פרימיטיב הסנכרון של מודל ה־interpreter-threads של Perl (use threads; use threads::shared). כאשר THING הוא משתנה שהוצהר כ־:shared - או הפניה לאוסף משותף - lock רוכש mutex מייעץ על אותו נתון לכל שאר הבלוק העוטף. כאשר הבלוק יוצא, המנעול משוחרר. כל thread אחר הקורא ל־lock על אותו נתון נחסם עד שהמחזיק משחרר אותו.
המנעול הוא מייעץ: הוא עוצר threads אחרים שגם הם קוראים ל־lock על אותו משתנה, ולא יותר מזה. thread שרק קורא או כותב את המשתנה המשותף מבלי לנעול אותו אינו נחסם ואינו מסודר בתור. lock הוא מוסכמה, לא מחסום.
תקציר#
lock $shared_scalar
lock @shared_array
lock %shared_hash
lock &shared_sub
lock $ref_to_shared_aggregate
מה מקבלים בחזרה#
הארגומנט עצמו:
עבור סקלר, ערך הסקלר.
עבור מערך, האש, או תת־שגרה, הפניה אליו.
ערך ההחזרה משמש לעיתים נדירות. lock נקרא בשל תופעת הלוואי שלו (רכישת ה־mutex), לא בשל ערכו.
המנעול הוא בעל־תחום, לא בעל־תחום־הוראה#
המנעול משוחרר כאשר הבלוק העוטף הפנימי ביותר יוצא, לא כאשר הוראת ה־lock מסתיימת. שתי השלכות:
lockבתוך בלוק{ ... }מסדר בתור רק למשך אותו בלוק. יציאה מהבלוק - על־ידי ירידה,return,last,die, או כל אופן אחר - משחררת את המנעול.לא ניתן לשחרר מנעול מוקדם. אין
unlock. אם אתם זקוקים למקטע קריטי קצר יותר, הציגו בלוק{ ... }הדוק יותר:{ lock %shared_hash; $shared_hash{$key} = $value; } # lock released here do_unlocked_work();
מילת מפתח חלשה#
lock הוא מילת מפתח חלשה. אם תת־שגרה בשם lock נמצאת בתחום בנקודת הקריאה (הוצהרה או יובאה לפני אתר הקריאה), אותה תת־שגרה נקראת במקום ה־built-in. זה הופך את lock לבטוח לשימוש כשם מתודה או פונקציה בקוד שאינו משתמש ב־threads. כדי לכפות את ה־built-in כאשר תת־שגרה באותו שם נמצאת בתחום, קראו לו דרך החבילה שלו: CORE::lock($thing).
דוגמאות#
סידור בתור של עדכון האש בין threads:
use threads;
use threads::shared;
my %counts :shared;
sub bump {
my $key = shift;
lock %counts;
$counts{$key}++;
}
נעילת אוסף דרך הפניה. lock עוקב אחר רמת הפניה אחת כדי למצוא את הנתון לנעילה:
my @queue :shared;
my $queue_ref = \@queue;
sub enqueue {
my $item = shift;
lock $queue_ref; # locks @queue
push @queue, $item;
}
צמצמו את המקטע הקריטי עם בלוק פנימי כך שעבודה לא־קשורה תרוץ ללא נעילה:
my $result;
{
lock %cache;
$result = $cache{$key};
}
expensive_post_processing($result); # runs without the lock
ללא use threads::shared, lock הוא no-op. זה הופך אותו ללא־מזיק להשארה בקוד שעלול לרוץ ב־single-thread:
# no `use threads::shared` here
lock $x; # does nothing, does not warn
מקרי קצה#
משתנה לא־משותף:
lockעל משתנה שלא הוצהר כ־:sharedואינו הפניה לנתונים משותפים אינו עושה דבר שימושי. תחת upstream זה עלול לבצע croak במקרים מסוימים; באחרים זה no-op בשקט. אל תסתמכו עלlockכדי לתפוס את הבאג ״שכחתי לשתף״.הפניה לעומת אוסף:
lock \%hו־lock %hשניהם נועלים את%h.lockמבצע dereference ברמה אחת כדי למצוא את הנתון המשותף.נעילה רקורסיבית בתוך thread אחד: אותו thread עשוי לרכוש מחדש מנעול שהוא כבר מחזיק; ה־mutex הוא רקורסיבי לכל thread. thread אחר עדיין נחסם.
אין
unlock: שחרור לפני יציאה מבלוק מצריך מבנה מחדש לתחום הדוק יותר. תבניותundef $lock_guardמשפות אחרות אינן חלות -lockאינו ערך שמאחסנים.עמימות של מילת־מפתח חלשה:
sub lockשהוצהר או יובא מוקדם יותר מנצח את ה־built-in. השתמשו ב־CORE::lockכדי לבטל את העמימות.לא חוצה־תהליכים:
lockמתאם threads בתוך תהליך מפענח אחד. לנעילה ברמת קובץ בין תהליכים השתמשו ב־flock; למנעולים מייעצים של מערכת קבצים אלה מנגנונים נפרדים ללא אינטראקציה.
הבדלים מ־upstream#
pperl אינו מממש interpreter threads. ה־opcode של
lockמזוהה על־ידי המפענח אך אין לו השפעת זמן ריצה - הוא מתנהג בדיוק כפי ש־lockשל upstream מתנהג בבנייה ללאthreads::shared: no-op שקט המחזיר את הארגומנט שלו. קוד המשתמש ב־lockבאופן הגנתי (כפי שתיעוד ה־upstream ממליץ לתאימות single-thread) רץ ללא שינוי. קוד התלוי ב־lockלסנכרון אמיתי לא יסונכרן תחת pperl.ההפצה הנלווית
threads::sharedאינה זמינה תחת pperl;use threads::sharedנכשל בזמן הידור. מקביליות ב־pperl נחשפת דרך אוטו־מקביליות של JIT (Rayon) על צורות לולאה ספציפיות, לא דרך ithreads גלויים למשתמש.
ראו גם#
flock- מנעול מייעץ על מטפל קובץ, מתאם בין תהליכים ולא בין threadswait- אוסף תהליך־ילד; המקבילה ברמת התהליך להמתנה ליחידת ביצוע אחרתfork- פרימיטיב המקביליות האחר ב־Perl הליבתי, ללא זיכרון משותף כברירת מחדלour- הצהרה על משתנה בתחום־חבילה; משויך בדרך כלל ל־:sharedבקוד מבוסס־threadslocal- קישור תחום־דינמי, לעיתים מבולבל עםlockמשום ששניהם מוגבלים־תחום ושניהם משחזרים ביציאה מבלוק