تاريخيًا، تمت كتابة جميع برامج الكمبيوتر بطريقة متسلسلة تمامًا. هذا سهل القراءة والكتابة والفهم. كما أنه من السهل أيضًا تشغيل الكمبيوتر ويتطلب أجهزة بسيطة نسبيًا. باستخدام نموذج التصميم هذا، فإن الطريقتين الوحيدتين لزيادة أداء النظام هما كتابة تعليمات برمجية أكثر كفاءة وزيادة سرعة وحدة المعالج. قد يكون من الممكن زيادة كفاءة الكود، لكنها بشكل عام عملية معقدة ذات نتائج محدودة في كثير من الأحيان.

لعقود من الزمن، يمكن أن ينخفض الأداء عن طريق انتظار وحدات المعالجة المركزية الجديدة الأكثر كفاءة. كما هو موضح في قانون مور، يتضاعف أداء وحدات المعالجة المركزية تقريبًا كل سنتين إلى ثلاث سنوات. لسوء الحظ، جاءت معظم مكاسب الأداء هذه من استخدام عقد تصنيع أصغر من أي وقت مضى. تكافح التكنولوجيا الحديثة لتقليل حجم العقدة بالمعدل التاريخي، وذلك بفضل الصعوبات المادية في العمل على مقياس نانومتر.

للتغلب على هذا، اختار مهندسو وحدة المعالج الحديثة إضافة نوى معالج متعددة إلى وحدات المعالجة المركزية. يمكن لكل نواة معالج العمل بشكل مستقل في مهمة مختلفة. بينما لا يمكنهم الجمع بين نفس المشكلة، يمكنهم العمل على مسألتين في وقت واحد. يوفر هذا التغيير المعماري الأساسي الكثير من الأداء الإضافي، ولكنه لا يفيد العمليات الفردية بشكل مباشر، على الرغم من أنه يقلل من التنازع على وقت المعالج.

للاستفادة من وحدات المعالجة المركزية متعددة النواة، يجب كتابة التعليمات البرمجية بطريقة متعددة الخيوط. يمكن بعد ذلك تشغيل كل مؤشر ترابط بشكل متزامن، مع توسيع نطاق الاستفادة من الأداء من خلال عدد الخيوط المتاحة ونواة وحدة المعالج. القيام بذلك، على الرغم من ذلك، يواجه تحديًا جديدًا، وهو “شرط السبق”

ملاحظة: لا يمكن أن تكون بعض المهام متعددة الخيوط، بينما يمكن أن تكون المهام الأخرى متعددة الخيوط على نطاق واسع. تعتمد فوائد الأداء المحتملة على العمل الذي يتم إنجازه.

شروط السباق

يمكن للبرامج متعددة الخيوط الاستفادة من النوى المتعددة. الأخطار كامنة في تلك المياه، وعلى استعداد للقبض على المبرمج عديم الخبرة. يمكن أن تحدث حالة السباق عندما يتفاعل موضوعان مختلفان مع نفس جزء الذاكرة.

مثال بسيط يمكن أن يكون خيطين يحاولان التحقق من متغير وزيادته في وقت واحد. دعنا نقول أن أ = 0. ثم يقوم خيطانان مختلفان بأداء وظائفهما، وفي مرحلة ما، يقومان بفحص a وزيادته بمقدار واحد. بشكل عام، تتوقع أن تكون نتيجة خيطين يضيفان واحدًا إلى صفر اثنين. في معظم الأحيان، يجب أن يكون هذا هو الحال. يمكنك الحصول على نتيجة مختلفة إذا مر كلا الموضوعين بهذه الوظيفة المحددة في الوقت المناسب بالضبط.

في هذه الحالة، يقرأ الخيط الأول قيمة. قبل أن يتمكن الخيط الأول من زيادة قيمة رغم ذلك، يقرأه مؤشر الترابط الثاني. الآن يضيف الخيط الأول واحدًا إلى صفر، لكن الخيط الثاني يعتقد بالفعل أن القيمة تساوي صفرًا، مضيفًا واحدًا إلى صفر. نتيجة ذلك أن القيمة النهائية لـ a هي 1، وليس 2.

السباق إلى أسوأ سيناريو

في حين أن المثال أعلاه قد لا يبدو سيئًا بشكل خاص، إلا أنه يمكن أن يكون له تأثيرات دراماتيكية. ماذا لو اختارت قيمة a طريقة تشغيل الآلة؟ ماذا لو كانت أنماط تشغيل معينة لهذه الآلة يمكن أن تكون خطيرة أو حتى مهددة للحياة؟

ظروف العرق أيضا لا تحتاج إلى أن تكون بهذه البساطة. على سبيل المثال، يمكن لخيط واحد أن يقرأ قسمًا من الذاكرة في نفس الوقت الذي يكتب فيه مؤشر ترابط آخر. في هذه الحالة، قد يحصل مؤشر ترابط القراءة على مزيج غريب من البيانات من قبل وبعد. لنفترض أن الشيك هو فحص بسيط للصواب / الخطأ.

إذا قال المتغير صحيح في بداية القراءة ولكن كان في طور الكتابة فوق الكلمة خطأ، فقد تكون نتيجة عملية القراءة شيئًا مثل “trlse”. هذا ليس “صحيحًا” أو “خطأ.” عدم كون أي من الخيارين في الخيار الثنائي سيؤدي بالتأكيد إلى تعطل التطبيق. يمكن أن يؤدي تلف الذاكرة هذا إلى العديد من المشكلات الأمنية، مثل رفض الخدمة وتصعيد الامتيازات.

قفل السباق

إن معرفة أجزاء الذاكرة المشتركة في البرنامج بين الخيوط المختلفة أمر ضروري لمنع حدوث حالة سباق. لا يلزم القيام بأي شيء إذا كان المتغير يتم التحكم فيه والوصول إليه فقط من خلال مؤشر ترابط واحد. إذا كان بإمكان خيطين أو أكثر الوصول إلى متغير، فيجب عليك التأكد من أن جميع العمليات على قطعة الذاكرة هذه قد اكتملت بشكل مستقل عن بعضها البعض.

يتحقق هذا الاستقلال بفضل القفل. في رمز البرنامج، تحتاج إلى وضع قفل عند كتابة وظيفة تعمل على جزء مشترك من الذاكرة. يمنع هذا القفل الخيوط الأخرى من الوصول إلى تلك القطعة من الذاكرة حتى يتم تحرير القفل.

القفل ليس من أكثر الحلول أناقة. لسبب واحد، أنه يحتوي على ذاكرة محمولة. ويمكنه أيضًا إجبار الخيط على التعليق، في انتظار تحرير القفل. اعتمادًا على الموقف، قد لا يتم تحرير القفل لفترة طويلة جدًا أو قد لا يتم تحريره على الإطلاق. في أسوأ السيناريوهات، قد يعتمد فتح القفل على شيء ما يحدث في خيط مغلق آخر، مما يؤدي إلى طريق مسدود.

من الضروري تحسين استخدام الأقفال. يمكنك التحكم في مدى دقة القفل. على سبيل المثال، إذا كنت تقوم بتحرير بيانات في جدول، فيمكنك قفل الجدول بأكمله أو قفل الصف الذي تم تحريره فقط. سيكون قفل الطاولة بأكملها عبارة عن قفل حبيبات خشن. إنه يقلل من الحمل من تنفيذ عدد كبير جدًا من الأقفال ولكنه يزيد من فرصة حظر خيط آخر بواسطة القفل. سيكون قفل الصف فقط بمثابة قفل دقيق. من غير المرجح أن يتداخل هذا مع الخيوط الأخرى، ولكن يعني أنه ستكون هناك حاجة إلى أقفال ممزقة، مما يزيد من إجمالي الحمل.

استنتاج

قفل الذاكرة هو أداة رمز يتم استخدامها لضمان الذرية في عمليات الذاكرة في بيئة متعددة الخيوط. من خلال قفل جزء من الذاكرة قبل تشغيلها، يمكنك التأكد من عدم حدوث أي سلوك غير متوقع بسبب حالة السباق. تأتي أقفال الذاكرة مصحوبة بذاكرة علوية ولكنها قد تسبب أيضًا حظرًا.

الحظر هو المكان الذي يحاول فيه مؤشر ترابط آخر العمل على وعاء مغلق. الخيط يجلس هناك، مغلق حتى يتم تحرير القفل. يمكن أن يتسبب هذا في حدوث مشكلات إذا كان تحرير القفل يتطلب خيطًا آخر للقيام بشيء ما، حيث قد يتم حظره قبل أن يتمكن من إكمال المتطلبات الأساسية لتحرير القفل الذي يحظره. يمكن تجنب أقفال الذاكرة عن طريق كتابة أكواد غير محظورة. ومع ذلك، يمكن أن يكون القيام بذلك معقدًا وأقل أداءً من استخدام الأقفال. لا تنسى ترك تعليقاتك أدناه.