سؤال

لذلك رأيت الكثير من المقالات التي تدعي الآن أنه على قفل C ++ Double Double، يستخدم عادة لمنع مواضيع متعددة من محاولة تهيئة Singleton التي تم إنشاؤها مكسوة، مكسورة. رمز قفل مزدوج عادي يقرأ مثل هذا:

class singleton {
private:
    singleton(); // private constructor so users must call instance()
    static boost::mutex _init_mutex;

public:
    static singleton & instance()
    {
        static singleton* instance;

        if(!instance)
        {
            boost::mutex::scoped_lock lock(_init_mutex);

            if(!instance)           
                instance = new singleton;
        }

        return *instance;
    }
};

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

أنا رأى اقتراحا لاستخدام مؤشر ترابط منطقي محلي وتحقق من ذلك بدلا من instance. وبعد شيء من هذا القبيل:

class singleton {
private:
    singleton(); // private constructor so users must call instance()
    static boost::mutex _init_mutex;
    static boost::thread_specific_ptr<int> _sync_check;

public:
    static singleton & instance()
    {
        static singleton* instance;

        if(!_sync_check.get())
        {
            boost::mutex::scoped_lock lock(_init_mutex);

            if(!instance)           
                instance = new singleton;

            // Any non-null value would work, we're really just using it as a
            // thread specific bool.
            _sync_check = reinterpret_cast<int*>(1);
        }

        return *instance;
    }
};

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

class singleton {
private:
    singleton(); // private constructor so users must call instance()
    static boost::mutex _init_mutex;

public:
    static singleton & instance()
    {
        static bool sync_check = false;
        static singleton* instance;

        if(!sync_check)
        {
            boost::mutex::scoped_lock lock(_init_mutex);

            if(!instance)           
                instance = new singleton;

            sync_check = true;
        }

        return *instance;
    }
};

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

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

إذن هل هذا العمل أم لا؟ لماذا ا؟

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

المحلول

لا يعمل الإصلاح الخاص بك على إصلاح أي شيء نظرا لأن الكتابة للمزامنة والمثيل يمكن القيام به من أجل حسب وحدة المعالجة المركزية. على سبيل المثال، تخيل أول دعوة إلى مثيل يحدث في نفس الوقت تقريبا على اثنين من وحدات المعالجة المركزية المختلفة. سيحصل الخيط الأول على القفل، قم بتهيئة المؤشر وتعيين Sync_check إلى True، في هذا الترتيب، ولكن قد يقوم المعالج بتغيير ترتيب الكتابة إلى الذاكرة. في وحدة المعالجة المركزية الأخرى، من الممكن أن تحقق الخيط الثاني للتحقق من المزامنة، راجع أنه صحيح، ولكن قد لا تتم الآن كتابة المثيل إلى الذاكرة. يرى اعتبارات البرمجة غير المقفلة ل Xbox 360 و Microsoft Windows للتفاصيل.

يجب أن يعمل الحل Sync_check الخاص بالخيط الذي ذكرته بعد ذلك (على افتراض أنك تهيئة مؤشرك إلى 0).

نصائح أخرى

هناك بعض القراءة الكبيرة حول هذا (على الرغم من أنها .NET / C # الموجهة إلى هنا: http://msdn.microsoft.com/en-us/magazine/cc163715.aspx.

ما يغلقه هو أن تكون بحاجة إلى أن تكون قادرا على إخبار وحدة المعالجة المركزية أنه لا يمكن إعادة ترتيب قراءتك / يكتب لهذا الوصول المتغير (منذ بنتيوم الأصلي، يمكن أن تعيد وحدة المعالجة المركزية إعادة ترتيب تعليمات معينة إذا كان يعتقد أن المنطق لن يتأثر )، وأنه يحتاج إلى ضمان أن تكون ذاكرة التخزين المؤقت متسقة (لا تنسى ذلك - نحن ديفون أن نتظاهر بأن جميع الذاكرة هي مجرد مورد مسطح واحد فقط، ولكن في الواقع، يحتوي كل CPU Core على ذاكرة التخزين المؤقت، بعض غير مشكلات (L1 )، قد يتم تقاسم البعض في بعض الأحيان (L2)) - قد يكتب Interizlization الخاص بك إلى ذاكرة الوصول العشوائي الرئيسية، ولكن قد يكون جوهر آخر القيمة غير المهجورة في ذاكرة التخزين المؤقت. إذا لم يكن لديك أي دلالات تزامن، فقد لا يعرف وحدة المعالجة المركزية أنه ذاكرة التخزين المؤقت قذرة.

لا أعرف الجانب C ++، ولكن في .NET، يمكنك تعيين المتغير كقليم من أجل حماية الوصول إليها (أو كنت تستخدم أساليب حاجز قراءة / كتابة الذاكرة في System.Threading).

جانبا، لقد قرأت أنه في .NET 2.0، مضمونة قفل مزدوج فحصه للعمل دون متغيرات "متقلبة" (لأي قراء .NET هناك) - لا يساعدك ذلك في رمز C ++ الخاص بك.

إذا كنت تريد أن تكون آمنا، فستحتاج إلى القيام المكافئ C ++ بمناسبة متغير مثل المتقلب في C #.

"القضية الأخيرة تكسر المصطلح - قد ينتهي مؤلمان بإنشاء Singleton".

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

بمجرد إنشاء المثيل، يتم إلغاء قفل MUTEX مؤشر ترابط الانتظار التالي سيؤدي إلى قفل Mutex ولكنه لن يحاول إنشاء مثيل جديد لأن الشيك سيفشل.

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

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

نسيت شيئا ما هنا؟

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

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