multithreading معنى ھل یقوم أحد تعلیمات المجمع بالتنفیذ دائما؟



what is thread (9)

لا يمكنك أن تفترض هذا. ما لم يذكر بوضوح في مواصفات المترجم. وعلاوة على ذلك لا أحد يستطيع أن يضمن أن واحد واحد تجميع التعليم في الواقع الذرية. في الممارسة العملية يتم ترجمة كل تعليمات المجمع إلى عدد من عملية ميكروكود - عفوا.
كما أن قضية حالة العرق تقترن بإحكام بنموذج الذاكرة (التماسك، والتسلسل، وتماسك الإفراج، وما إلى ذلك)، لكل إجابة ونتائج يمكن أن تكون مختلفة.

اليوم جئت عبر هذا السؤال:

لديك رمز

static int counter = 0;
void worker() {
    for (int i = 1; i <= 10; i++)
        counter++;
}

إذا كان سيتم استدعاء worker من اثنين من المواضيع المختلفة، ما هي قيمة سوف يكون لها بعد أن انتهت كل منهما؟

وأنا أعلم أنه في الواقع يمكن أن يكون أي شيء. ولكن الشجاعة الداخلية تقول لي، أن counter++ سوف تترجم على الأرجح إلى تعليمات المجمع الواحد، وإذا كان كل من الخيوط تنفذ على نفس الأساسية، سيكون counter 20.

ولكن ماذا لو تم تشغيل هذه المواضيع على النوى أو المعالجات المختلفة، هل يمكن أن يكون هناك شرط السباق في ميكروكود؟ هل يمكن دائما النظر إلى تعليم المجمع على أنه عملية ذرية؟


ليس دائما - في بعض المعماريات يتم ترجمة تعليمات التجميع إلى التعليمات البرمجية للماكينة الواحدة، بينما لا يتم ترجمتها على الآخرين.

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

استخدام تقنيات المزامنة المناسبة بدلا من ذلك، تعتمد على اللغة التي يتم الترميز في.


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

لهذا الغرض، هناك حواجز الذاكرة ( http://en.wikipedia.org/wiki/Memory_barrier )

وباختصار، في حين أن التعليمات الذرية كافية على إنتل (مع البادئات القفل ذات الصلة)، يجب أن يتم المزيد على غير إنتل، لأن الذاكرة I / O قد لا تكون في نفس الترتيب.

هذه مشكلة معروفة عند نقل حلول "خالية من القفل" من إنتل إلى أبنية أخرى.

(لاحظ أن أنظمة متعددة المعالجات (وليس متعددة النواة) على x86 يبدو أيضا بحاجة إلى حواجز الذاكرة، على الأقل في وضع 64 بت.


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


الجواب هو، فإنه يعتمد!

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

ومع ذلك، فإن السؤال يتلخص هو جهاز واحد الذرية الذرية؟

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

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

الجواب هو، فإنه يعتمد. اقرأ بعناية دليل إرشادات الماكينة الخاص بالمورد. في شك، فإنه ليس!

تحرير: أوه، رأيت ذلك الآن، يمكنك أيضا طلب ++ عداد. ولا يمكن الوثوق على الإطلاق بعبارة "من المرجح ترجمتها". هذا يعتمد إلى حد كبير أيضا على مترجم بالطبع! فإنه يصبح أكثر صعوبة عندما يقوم مترجم بتحسينات مختلفة.


في معظم الحالات، لا . في الواقع، على x86، يمكنك تنفيذ التعليمات

push [address]

والتي، في C، سيكون شيئا مثل:

*stack-- = *address;

يؤدي هذا نقل الذاكرة اثنين في تعليمات واحدة .

هذا من المستحيل أساسا القيام به في 1 دورة على مدار الساعة، وليس أقلها لأن نقل الذاكرة واحد هو أيضا غير ممكن في دورة واحدة!


أعتقد أنك ستحصل على حالة سباق على الوصول.

إذا كنت ترغب في ضمان عملية ذرية في زيادة عداد ثم كنت بحاجة إلى استخدام ++ العداد.


على وجه التحديد ل x86، وفيما يتعلق بك المثال: counter++ ، وهناك عدد من الطرق التي يمكن تجميعها. المثال الأكثر تافهة هو:

inc counter

وهذا يترجم إلى العمليات الجزئية التالية:

  • تحميل counter إلى سجل مخفي على وحدة المعالجة المركزية
  • زيادة السجل
  • تخزين السجل المحدث في counter

هذا هو أساسا نفس:

mov eax, counter
inc eax
mov counter, eax

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

إذا كنت تريد التأكد من أن هذا inc ذري، استخدم بادئة lock :

lock inc counter

lock يضمن أن لا أحد يستطيع تحديث counter بين التحميل والمخزن.

فيما يتعلق بالتعليمات الأكثر تعقيدا، لا يمكنك عادة افتراض أنها ستنفذ ذريا، إلا إذا كانت تدعم بادئة lock .


غير صالح من قبل تعليق ناثان: إذا كنت أتذكر بلدي إنتل x86 المجمع بشكل صحيح، تعليمات إنك يعمل فقط للسجلات ولا يعمل مباشرة لمواقع الذاكرة.

لذا فإن عداد ++ لن يكون تعليمات واحدة في المجمع (مجرد تجاهل جزء ما بعد الزيادة). سيكون على الأقل ثلاثة تعليمات: تحميل متغير عداد للتسجيل، سجل الزيادة، تحميل سجل العودة إلى العداد. وهذا هو فقط ل x86 الهندسة المعمارية.

باختصار، لا تعتمد على كونها ذرية ما لم يتم تحديدها من قبل مواصفات اللغة وأن المحول البرمجي الذي تستخدمه يدعم المواصفات.





race-condition