قفل إعادة الدخول
في علوم الحاسوب ، يعتبر القفل المتبادل القابل لإعادة الدخول (المعروف أيضًا باسم القفل المتبادل المتكرر أو القفل المتكرر ) أداة تزامن بدائية يمكن قفلها عدة مرات بواسطة نفس الخيط دون التسبب في حالة جمود .
بينما يُؤدي محاولة قفل مُؤقت قياسي (غير قابل لإعادة الدخول) يمتلكه الخيط بالفعل إلى حظر غير مُحدد، تنجح هذه العملية مع مُؤقت قابل لإعادة الدخول. ويتحقق ذلك بربط المُؤقت بالخيط الذي يمتلكه، مع الاحتفاظ بعداد للقفل. يستطيع الخيط المالك الحصول على القفل عدة مرات، مُزيدًا العداد في كل مرة. ولا يُحرر القفل للخيوط الأخرى إلا بعد أن يُحرره الخيط المالك بنفس عدد مرات الحصول عليه، ليصل العداد إلى الصفر.
تحفيز
يحلّ القفل المتبادل القابل لإعادة الدخول حالات الجمود التي قد تحدث عندما تحتاج دالة ما إلى الحصول على قفل محتجز بالفعل بواسطة نفس الخيط. يحدث هذا غالبًا في التعليمات البرمجية المتكررة أو عندما تستدعي دالةٌ ما، بعد حصولها على قفل، دالة أخرى يجب أن تحصل على نفس القفل. [ 1 ]
تُعالج الأقفال المتبادلة المتكررة مشكلة عدم إمكانية إعادة الدخول في الأقفال المتبادلة العادية: إذا تم استدعاء دالة تأخذ قفلًا وتنفذ دالة رد نداء من قِبل دالة رد النداء نفسها، فسيحدث تعطل . [ 2 ] في الشفرة الزائفة ، يكون الوضع كما يلي:
ضع في اعتبارك السيناريو التالي في الشفرة الزائفة :
var m : Mutex // متبادل قياسي غير قابل لإعادة الدخول، مفتوح مبدئيًا. دالة lock_and_call(i : عدد صحيح) m.lock() callback(i) m.unlock() دالة رد الاتصال (i : عدد صحيح) إذا كان i > 0 lock_and_call(i - 1) lock_and_call(1) // استدعاء الدالة
عند تنفيذ الدالة lock_and_call(1) باستخدام mutex قياسي، ينتج عن ذلك حالة جمود:
- تنجح عملية الاستدعاء الأولية لـ lock_and_call(1) في الحصول على القفل m .
- ثم يستدعي الدالة callback(1) .
- داخل دالة الاستدعاء (1) ، ولأن i > 0 ، فإنها تستدعي دالة القفل والاستدعاء (0) .
- يحاول هذا الاستدعاء الثاني للدالة lock_and_call الحصول على القفل m مرة أخرى.
- حالة التعطل : نظرًا لأن القفل المتبادل m مُقفل بالفعل، يتوقف الخيط وينتظر تحرير القفل. ومع ذلك، فإن الخيط نفسه هو الذي يحتفظ بالقفل، لذا فهو ينتظر نفسه لإكمال إجراء لا يمكنه القيام به أبدًا.
يمنع استخدام قفل تبادلي قابل لإعادة الدخول للدالة m حدوث هذا التعطل. عند محاولة استدعاء الدالة lock_and_call(0) للمرة الثانية قفل القفل التبادلي، تنجح العملية لأن الخيط الذي يحاول الحصول على القفل هو المالك بالفعل. يتم زيادة العداد الداخلي للقفل التبادلي. لا يتم تحرير القفل بالكامل إلا بعد اكتمال كلا الاستدعاءين للدالة lock_and_call وتنفيذ عمليات m.unlock() المقابلة لهما.
الاستخدام العملي
يشير دبليو ريتشارد ستيفنز إلى أن استخدام الأقفال المتكررة بشكل صحيح أمر "معقد"، ويوصي باستخدامها لتكييف التعليمات البرمجية أحادية الخيوط دون تغيير واجهات برمجة التطبيقات ، ولكن "فقط عندما لا يكون هناك حل آخر ممكن". [ 3 ]
تستخدم آلية التزامن الأصلية في لغة جافا، المسماة monitor ، الأقفال المتكررة. من الناحية التركيبية، القفل عبارة عن كتلة برمجية مسبوقة بالكلمة المفتاحية 'synchronized'، ويسبقها أي مرجع لكائن Object بين قوسين يُستخدم كقفل تبادلي (mutex). داخل كتلة synchronized، يمكن استخدام الكائن المُعطى كمتغير شرطي عن طريق تنفيذ wait() أو notify() أو notifyAll() عليه. وبالتالي، فإن جميع الكائنات هي في الوقت نفسه أقفال تبادلية متكررة ومتغيرات شرطية . [ 4 ]
مثال
- يستدعي الخيط A الدالة F التي تحصل على قفل قابل لإعادة الدخول لنفسها قبل المتابعة.
- يستدعي الخيط B الدالة F التي تحاول الحصول على قفل قابل لإعادة الدخول لنفسها، لكنها لا تستطيع ذلك بسبب وجود قفل معلق بالفعل، مما يؤدي إما إلى حظر (حيث تنتظر)، أو إلى انتهاء المهلة إذا طُلب ذلك.
- يستدعي الخيط A، من خلال F، نفسه بشكل متكرر. يمتلك الخيط A القفل بالفعل، لذا لن يعيق نفسه (لا يوجد تعطل). هذه هي الفكرة الأساسية للقفل المتبادل القابل لإعادة الدخول، وهي ما يميزه عن القفل العادي.
- لا يزال الخيط F الخاص بالخيط B ينتظر، أو أنه قد رصد انتهاء المهلة الزمنية وتجاوزه.
- ينتهي الخيط A's F ويحرر قفله (أقفاله)
- يمكن الآن للخيط B's F الحصول على قفل إعادة الدخول والمتابعة إذا كان لا يزال في حالة انتظار
محاكاة البرمجيات
يمكن تحقيق محاكاة البرامج باستخدام البنية التالية:
- شرط "التحكم" باستخدام قفل عادي
- معرّف المالك، فريد لكل سلسلة (افتراضيًا فارغ / غير مُعيّن)
- عدد عمليات الاستحواذ (القيمة الافتراضية هي صفر)
اكتساب
- الحصول على حالة التحكم.
- إذا تم تعيين المالك وليس الخيط الحالي، فانتظر حتى يتم إخطارك بشرط التحكم (وهذا أيضًا يحرر الشرط).
- قم بتعيين المالك إلى سلسلة العمليات الحالية. يجب أن يكون مُعرّف المالك قد تم مسحه بالفعل في هذه المرحلة ما لم يكن المُستحوذ هو المالك بالفعل.
- قم بزيادة عدد عمليات الاستحواذ (يجب أن تكون النتيجة دائمًا 1 للمالكين الجدد).
- قم بإلغاء شرط التحكم.
يطلق
- الحصول على شرط السيطرة، مع التأكيد على أن المالك هو المُحرِّر.
- قم بإنقاص عدد عمليات الاستحواذ، مع التأكيد على أن العدد أكبر من أو يساوي الصفر.
- إذا كان عدد عمليات الاستحواذ صفرًا، فقم بمسح معلومات المالك وأبلغ عن حالة التحكم.
- قم بإلغاء شرط التحكم.
مراجع
- ↑ بوشمان، فرانك؛ هيني، كيفلين؛ شميدت، دوغلاس سي. (2007). هندسة البرمجيات الموجهة بالأنماط: لغة أنماط للحوسبة الموزعة . جون وايلي وأولاده. ص 374. ISBN 9780470065303.
- ↑ بوشمان، فرانك؛ هيني، كيفلين؛ شميدت، دوغلاس سي. (2007). هندسة البرمجيات الموجهة بالأنماط: لغة أنماط للحوسبة الموزعة . جون وايلي وأولاده. ص 374. ISBN 9780470065303.
- ↑ ستيفنز، دبليو. ريتشارد؛ راغو، ستيفن أ. (2013). البرمجة المتقدمة في بيئة يونكس . أديسون-ويسلي. ص 434.
- ↑ ديفيد هوفماير. "المحاضرة 17: سلاسل جافا، التزامن" . CS 365 - الحوسبة المتوازية والموزعة . مؤرشف من الأصل في 16 فبراير 2015. تم الاطلاع عليه في 4 يونيو 2015 .
{{cite book}}تم|work=تجاهله ( مساعدة )
- التحكم في التزامن
- أنماط تصميم البرمجيات
- الخيوط (الحوسبة)
