سؤال

لنفترض أن لدي "عداد" متغير، وهناك عدة سلاسل للوصول إلى قيمة "العداد" وتعيينها باستخدام Interlocked، على سبيل المثال:

int value = Interlocked.Increment(ref counter);

و

int value = Interlocked.Decrement(ref counter);

هل يمكنني أن أفترض أن التغيير الذي أجراه Interlocked سيكون مرئيًا في جميع المواضيع؟

إذا لم يكن الأمر كذلك، فماذا علي أن أفعل لجعل كافة المواضيع مزامنة المتغير؟

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

عندما قرأت المساعدة عبر الإنترنت، قيل لي: "لا ينبغي عادةً تمرير الحقل المتقلب باستخدام معلمة ref أو out".

هل كانت مفيدة؟

المحلول

وInterlockedIncrement / التناقص على وحدات المعالجة المركزية إلى x86 (وإلى x86 قفل إضافة / ديسمبر) وخلق تلقائيا <م> حاجز الذاكرة الذي يعطي الرؤية لجميع المواضيع (أي، يمكن لجميع المواضيع يرى تقرير لها كما في النظام، مثل متسلسلة الاتساق الذاكرة). حاجز الذاكرة يجعل جميع الأحمال الذاكرة / مخازن في انتظار أن تكتمل. لا علاقة volatile على هذا السؤال على الرغم من C # و Java (وبعض / المجمعين C ++ C) فرض volatile لجعل حاجز الذاكرة. ولكن، عملية متشابكة بالفعل حاجز الذاكرة عن طريق وحدة المعالجة المركزية.

يرجى أيضا أن تأخذ نظرة جوابي آخر في ستاكوفيرفلوو.

ملحوظة التي لديها وأفترض أن C # الصورة InterlockedIncrement / إنقاص ورسم الخرائط جوهري إلى إلى x86 وقفل إضافة / ديسمبر

نصائح أخرى

<اقتباس فقرة>   

هل أفترض أن التغيير التي أدلى بها مشابك سوف تكون واضحة في كل المواضيع؟

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

وكبديل (والكثير يفضل IMO)، قراءتها باستخدام تعليمات متشابكة آخر. هذا وسوف نرى دائما القيمة التي تم تحديثها في كل المواضيع:

int readvalue = Interlocked.CompareExchange(ref counter, 0, 0);

والتي ترجع قراءة قيمة، وإذا كان 0 مقايضة مع 0.

والدافع: التحذير يلمح أن شيئا ما ليس صحيحا. الجمع بين الطريقتين (متقلبة ومشابك) ليست الطريقة المقصودة للقيام بذلك.

تحديث: يبدو أن نهج آخر لموثوقية 32 بت يقرأ دون استخدام "متقلبة" هو عن طريق استخدام Thread.VolatileRead كما هو مقترح في هذه الإجابة . وهناك أيضا بعض الأدلة على أن أكون مخطئا تماما حول استخدام Interlocked 32 بت يقرأ، على سبيل المثال <لأ href = "http://connect.microsoft.com/VisualStudio/feedback/details/256490/interlocked-read-only- لا استمرت البيانات من نوع "يختلط =" نوفولو noreferrer "> هذه المسألة ربط ، على الرغم من أنني أتساءل عما إذا كان التمييز متحذلق قليلا في الطبيعة.

وماذا يعني في الواقع هو: لا تستخدم هذه الإجابة كمصدر وحيد الخاص بك؛ أواجه شكوكي حول هذا الموضوع.

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

ومشابك يضمن أن 1 فقط موضوع في وقت واحد يمكن تحديث القيمة. للتأكد من المواضيع الأخرى يمكن قراءة القيمة الصحيحة (وليس قيمة المخبأة) علامة على أنها متقلبة.

والعامة عداد كثافة العمليات المتقلبة؛

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

فيما يلي مثال على التعليمات البرمجية التي لن تنتهي أبدًا بسبب JIT.(تم التعديل من حواجز الذاكرة في .NET ليكون برنامج LINQPad قابلاً للتشغيل ومحدثًا للسؤال).

// Run this as a LINQPad program in "Release Mode".
// ~ It will never terminate on .NET 4.5.2 / x64. ~
// The program will terminate in "Debug Mode" and may terminate
// in other CLR runtimes and architecture targets.
class X {
    // Adding {volatile} would 'fix the problem', as it prevents the JIT
    // optimization that results in the non-terminating code.
    public int terminate = 0;
    public int y;

    public void Run() {
        var r = new ManualResetEvent(false);
        var t = new Thread(() => {
            int x = 0;
            r.Set();
            // Using Volatile.Read or otherwise establishing
            // an Acquire Barrier would disable the 'bad' optimization.
            while(terminate == 0){x = x * 2;}
            y = x;
        });

        t.Start();
        r.WaitOne();
        Interlocked.Increment(ref terminate);
        t.Join();
        Console.WriteLine("Done: " + y);
    }
}

void Main()
{
    new X().Run();
}

الشرح من حواجز الذاكرة في .NET:

هذه المرة هو JIT، وليس الأجهزة. من الواضح أن JIT قام بتخزين قيمة المتغير إنهاء مؤقتًا [في سجل EAX والبرنامج] عالق الآن في الحلقة الموضحة أعلاه..

إما باستخدام أ lock أو إضافة أ Thread.MemoryBarrier داخل الحلقة while سوف يحل المشكلة.أو يمكنك حتى استخدامها Volatile.Read [أو أ volatile مجال]. الغرض من حاجز الذاكرة هنا هو منع تحسينات JIT فقط. والآن بعد أن رأينا كيف يمكن للبرامج والأجهزة إعادة ترتيب عمليات الذاكرة, ، حان الوقت لمناقشة حواجز الذاكرة..

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

إضافة volatile هنا سوف يمنع تحسين JIT، وبالتالي "إصلاح المشكلة"، حتى لو أدى ذلك إلى ظهور تحذير.ويمكن أيضًا تصحيح هذا البرنامج من خلال استخدام Volatile.Read أو إحدى العمليات الأخرى المختلفة التي تسبب الحاجز:تعد هذه الحواجز جزءًا من صحة برنامج CLR/JIT مثل أسوار ذاكرة الأجهزة الأساسية.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top