أمان الخيوط
في برمجة الحاسوب متعددة الخيوط ، تُعتبر الدالة آمنة للخيوط عندما يمكن استدعاؤها أو الوصول إليها بشكل متزامن من قِبل خيوط متعددة دون التسبب في سلوك غير متوقع، أو حالات تضارب ، أو تلف البيانات . [ 1 ] [ 2 ] وكما هو الحال في سياق تعدد الخيوط حيث يُنفذ البرنامج عدة خيوط في وقت واحد في مساحة عناوين مشتركة ، ولكل خيط من هذه الخيوط إمكانية الوصول إلى ذاكرة كل خيط آخر ، فإن الدوال الآمنة للخيوط يجب أن تضمن أن جميع هذه الخيوط تعمل بشكل صحيح وتفي بمواصفات تصميمها دون تفاعل غير مقصود. [ 3 ]
توجد استراتيجيات متنوعة لإنشاء هياكل بيانات آمنة للاستخدام المتزامن. [ 3 ]
مستويات أمان الخيوط
يستخدم البائعون المختلفون مصطلحات مختلفة قليلاً فيما يتعلق بسلامة الخيوط، [ 4 ] ولكن المصطلحات الأكثر شيوعًا لسلامة الخيوط هي: [ 2 ]
- غير آمن للاستخدام المتزامن : لا ينبغي الوصول إلى هياكل البيانات في وقت واحد بواسطة خيوط مختلفة.
- آمن للخيوط، التسلسل : يستخدم mutex واحدًا لجميع الموارد لضمان خلو الخيط من حالات التزامن عندما يتم الوصول إلى تلك الموارد بواسطة خيوط متعددة في وقت واحد.
- آمن للخيوط، آمن للخيوط المتعددة : يستخدم mutex لكل مورد على حدة لضمان خلو الخيط من حالات التزامن عندما يتم الوصول إلى تلك الموارد بواسطة خيوط متعددة في وقت واحد.
تتضمن ضمانات سلامة الخيوط عادةً خطوات تصميمية لمنع أو الحد من مخاطر حالات التعطل المختلفة ، بالإضافة إلى تحسينات لزيادة الأداء المتزامن إلى أقصى حد. ومع ذلك، لا يمكن دائمًا تقديم ضمانات خالية من حالات التعطل، لأن حالات التعطل قد تحدث بسبب عمليات الاستدعاء وانتهاك طبقات البنية بغض النظر عن المكتبة نفسها.
يمكن لمكتبات البرامج توفير ضمانات معينة لسلامة الخيوط. [ 5 ] على سبيل المثال، قد تكون عمليات القراءة المتزامنة مضمونة السلامة في بيئة الخيوط، بينما قد لا تكون عمليات الكتابة المتزامنة كذلك. وتعتمد سلامة الخيوط في برنامج يستخدم مكتبةً ما على استخدامه للمكتبة بطريقة تتوافق مع تلك الضمانات.
أساليب التنفيذ
فيما يلي فئتان من الأساليب لتجنب حالات التزامن لتحقيق سلامة الخيوط.
يركز النوع الأول من المناهج على تجنب الحالة المشتركة ويتضمن ما يلي:
- إعادة الدخول [ 6 ]
- كتابة التعليمات البرمجية بطريقة تسمح بتنفيذها جزئيًا بواسطة خيط برمجي، أو تنفيذها بالكامل بواسطة نفس الخيط، أو تنفيذها بالتزامن بواسطة خيط برمجي آخر مع ضمان إتمام التنفيذ الأصلي بشكل صحيح. يتطلب ذلك حفظ معلومات الحالة في متغيرات محلية لكل عملية تنفيذ، عادةً على مكدس، بدلاً من المتغيرات الثابتة أو العامة أو أي حالة غير محلية أخرى. يجب الوصول إلى جميع الحالات غير المحلية من خلال عمليات ذرية، كما يجب أن تكون هياكل البيانات قابلة لإعادة الاستخدام.
- التخزين المحلي للخيط
- تُحفظ المتغيرات محليًا بحيث يمتلك كل خيط نسخة خاصة به. وتحتفظ هذه المتغيرات بقيمها عبر الإجراءات الفرعية وحدود التعليمات البرمجية الأخرى، وهي آمنة للاستخدام في بيئة متعددة الخيوط لأنها محلية لكل خيط، حتى وإن تم تنفيذ التعليمات البرمجية التي تصل إليها في الوقت نفسه بواسطة خيط آخر.
- الكائنات غير القابلة للتغيير
- لا يمكن تغيير حالة الكائن بعد إنشائه. وهذا يعني أن البيانات المُشاركة هي بيانات للقراءة فقط، وأن أمان الخيوط مُتحققٌ تلقائيًا. بالتالي، يُمكن تنفيذ العمليات القابلة للتغيير (غير الثابتة) بحيث تُنشئ كائنات جديدة بدلًا من تعديل الكائنات الموجودة. هذا الأسلوب مميز للبرمجة الوظيفية ، ويُستخدم أيضًا في تطبيقات السلاسل النصية في جافا وسي شارب وبايثون . (انظر: الكائن غير القابل للتغيير ).
أما الفئة الثانية من الأساليب فهي متعلقة بالتزامن، وتستخدم في الحالات التي لا يمكن فيها تجنب الحالة المشتركة:
- الاستبعاد المتبادل
- يتم الوصول إلى البيانات المشتركة بشكل متسلسل باستخدام آليات تضمن أن يقوم خيط واحد فقط بقراءة أو كتابة البيانات المشتركة في أي وقت. يجب دراسة دمج مبدأ الاستبعاد المتبادل بعناية، لأن الاستخدام غير السليم قد يؤدي إلى آثار جانبية مثل حالات التعطل التام ، وحالات التعطل الجزئي ، ونقص الموارد .
- العمليات الذرية
- يتم الوصول إلى البيانات المشتركة باستخدام عمليات ذرية لا يمكن مقاطعتها بواسطة خيوط أخرى. يتطلب هذا عادةً استخدام تعليمات خاصة بلغة الآلة ، والتي قد تكون متوفرة في مكتبة وقت التشغيل . ولأن العمليات ذرية، تبقى البيانات المشتركة دائمًا في حالة صالحة، بغض النظر عن كيفية وصول الخيوط الأخرى إليها. تُشكل العمليات الذرية أساس العديد من آليات قفل الخيوط، وتُستخدم لتنفيذ بدائيات الاستبعاد المتبادل.
أمثلة
في جزء كود جافا التالي ، تجعل الكلمة المفتاحية synchronized في جافا الطريقة آمنة للاستخدام في بيئة متعددة الخيوط:
class Counter { private int i = 0 ;public synchronized void inc () { i ++ ; } }في لغة البرمجة C ، يمتلك كل خيط مكدسه الخاص. مع ذلك، لا يُخزَّن المتغير الثابت في المكدس، بل تتشارك جميع الخيوط الوصول إليه في آنٍ واحد. إذا تداخلت عدة خيوط أثناء تشغيل نفس الدالة، فمن المحتمل أن يُغيّر أحد الخيوط قيمة متغير ثابت بينما يقوم خيط آخر بفحصه. يُطلق على هذا الخطأ المنطقي ، الذي يصعب تشخيصه والذي قد يُترجم ويُنفَّذ بشكل صحيح في معظم الأحيان، اسم حالة التزامن . إحدى الطرق الشائعة لتجنب ذلك هي استخدام متغير مشترك آخر كـ "قفل" أو "مُستَقبِل مُتبادل " ( mutex ).
في جزء كود C التالي الذي يستدعي رؤوس POSIX ، تكون الدالة آمنة للاستخدام في بيئة متعددة الخيوط، ولكنها غير قابلة لإعادة الدخول:
#include <pthread.h>int incrementCounter () { static int counter = 0 ; static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER ;// السماح لخيط واحد فقط بالزيادة في كل مرة pthread_mutex_lock ( & mutex );++ العداد ;// تخزين القيمة قبل أن تقوم أي خيوط أخرى بزيادتها int result = counter ;pthread_mutex_unlock ( & mutex );أعد النتيجة ؛ }في المثال أعلاه، increment_counterيمكن استدعاء الدالة من قبل خيوط متعددة دون أي مشكلة، حيث يُستخدم قفل التزامن (mutex) لمزامنة جميع عمليات الوصول إلى counterالمتغير المشترك. ولكن إذا استُخدمت الدالة في معالج مقاطعة قابل لإعادة الدخول ، وحدثت مقاطعة ثانية أثناء قفل التزامن، فسيتوقف الروتين الثاني عن العمل نهائيًا. ولأن معالجة المقاطعات قد تُعطّل مقاطعات أخرى، فقد يتأثر النظام بأكمله.
يمكن تنفيذ نفس الوظيفة لتكون آمنة للاستخدام في بيئة متعددة الخيوط وقابلة لإعادة الدخول باستخدام العمليات الذرية الخالية من الأقفال ، والتي تم تقديمها في C++11 :
استيراد std ؛باستخدام std :: atomic ;int incrementCounter () { static atomic < int > counter ( 0 );// يُضمن أن تتم الزيادة بشكل ذري int result = ++ counter ;أعد النتيجة ؛ }انظر أيضاً
مراجع
- ↑Kerrisk, Michael (2010). The Linux Programing Interface. No Starch Press. p. 699, "Chapter 31: THREADS: THREAD SAFETY AND PER-THREAD STORAGE"
{{cite book}}: CS1 maint: postscript (link) - 12Oracle (2010-11-01). "Oracle: Thread safety". Docs.oracle.com. Retrieved 2013-10-16"A procedure is thread safe when the procedure is logically correct when executed simultaneously by several threads"; "3 level of thread-safe"
{{cite web}}: CS1 maint: postscript (link) - 12"Multithreaded Programming Guide: Chapter 7 Safe and Unsafe Interfaces". Docs Oracle. Oracle. November 2020. Retrieved 2024-04-30; "Thread Safety"
{{cite web}}: CS1 maint: postscript (link) - ↑"API thread safety classifications". IBM. 2023-04-11. Retrieved 2023-10-09.
- ↑"MT Safety Levels for Libraries". Docs Oracle. Retrieved 2024-05-17.
- ↑"Reentrancy and Thread-Safety | Qt 5.6". Qt Project. Retrieved 2016-04-20.
External links
- Java Q&A Experts (20 April 1999). "Thread-safe design (4/20/99)". JavaWorld.com. Retrieved 2012-01-22.
- TutorialsDesk (30 Sep 2014). "Synchronization and Thread Safety Tutorial with Examples in Java". TutorialsDesk.com. Retrieved 2012-01-22.
- Venners, Bill (1 August 1998). "Design for thread safety". JavaWorld.com. Retrieved 2012-01-22.
- Suess, Michael (15 October 2006). "A Short Guide to Mastering Thread-Safety". Thinking Parallel. Retrieved 2012-01-22.
- Threads (computing)
- Programming language topics
