سؤال

لا أحد يعرف تماما مؤشر الترابط-الآمن shared_ptr التنفيذ ؟ E. g.تعزيز تنفيذ shared_ptr هو مؤشر الترابط-الآمن للأهداف (refcounting) و أيضا آمنة في وقت واحد shared_ptr سبيل المثال يقرأ لكن لا يكتب أو القراءة/الكتابة.

(انظر دفعة مستندات, أمثلة 3 و 4 و 5).

هل هناك shared_ptr تنفيذ تماما موضوع آمنة shared_ptr الحالات ؟

الغريب أن تعزز مستندات تقول أن:

shared_ptr الكائنات تقدم نفس المستوى من موضوع السلامة المدمج في أنواع.

ولكن إذا قارنت عادي مؤشر (المدمج في نوع) إلى smart_ptr, ثم في وقت واحد يكتب عادي مؤشر الترابط-الآمن ، ولكن في وقت واحد يكتب إلى smart_ptr لا.

تحرير:أعني قفل مجانا على تنفيذ المعمارية x86.

EDIT2:مثال حالة استخدام هذا المؤشر الذكية حيث سيكون هناك عدد من المواضيع عامل أي تحديث عالمي shared_ptr مع عملهم الحالي البند و شاشة الخيط الذي يأخذ عينات عشوائية من عناصر العمل.المشتركة-ptr تملك عنصر العمل حتى آخر عنصر العمل مؤشر المسندة إليها (وبالتالي تدمير السابقة عنصر العمل).الشاشة سوف تحصل على ملكية عنصر العمل (وبالتالي منع عنصر العمل ليتم تدميرها) بتخصيص الخاصة المشتركة-ptr.ويمكن أن يتم ذلك مع XCHG و الحذف اليدوي ، ولكن سيكون من الرائع إذا كان مشترك-ptr يمكن أن تفعل ذلك.

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

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

المحلول

من المحتمل أن تؤثر إضافة العوائق اللازمة لمثل هذا التنفيذ Share_ptr الآمن تمامًا لمؤشر الترابط على الأداء.خذ بعين الاعتبار السباق التالي (ملاحظة:الكود الزائف يزخر):

الموضوع 1:global_ptr = أ؛

الموضوع 2:global_ptr = ب؛

الموضوع 3:local_ptr = global_ptr;

إذا قمنا بتقسيم هذا إلى العمليات التأسيسية:

الموضوع 1:

A.refcnt++;
tmp_ptr = exchange(global_ptr, A);
if (!--tmp_ptr.refcnt) delete tmp_ptr;

الموضوع 2:

B.refcnt++;
tmp_ptr = exchange(global_ptr, B);
if (!--tmp_ptr.refcnt) delete tmp_ptr;

الموضوع 3:

local_ptr = global_ptr;
local_ptr.refcnt++;

من الواضح أنه إذا قرأ الخيط 3 المؤشر بعد تبديل A، ثم يذهب B ويحذفه قبل زيادة عدد المرجع، فستحدث أشياء سيئة.

للتعامل مع هذا، نحتاج إلى قيمة وهمية لاستخدامها أثناء قيام مؤشر الترابط 3 بإجراء تحديث refcnt:(ملحوظة:Compare_exchange(متغير، متوقع، جديد) يستبدل ذريًا القيمة في المتغير بجديد إذا كانت تساوي حاليًا جديدًا، ثم يُرجع صحيحًا إذا نجح في ذلك)

الموضوع 1:

A.refcnt++;
tmp_ptr = global_ptr;
while (tmp_ptr == BAD_PTR || !compare_exchange(global_ptr, tmp_ptr, A))
    tmp_ptr = global_ptr;
if (!--tmp_ptr.refcnt) delete tmp_ptr;

الموضوع 2:

B.refcnt++;
while (tmp_ptr == BAD_PTR || !compare_exchange(global_ptr, tmp_ptr, A))
    tmp_ptr = global_ptr;
if (!--tmp_ptr.refcnt) delete tmp_ptr;

الموضوع 3:

tmp_ptr = global_ptr;
while (tmp_ptr == BAD_PTR || !compare_exchange(global_ptr, tmp_ptr, BAD_PTR))
    tmp_ptr = global_ptr;
local_ptr = tmp_ptr;
local_ptr.refcnt++;
global_ptr = tmp_ptr;

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

هذه التكلفة، التي يجب أن تتحملها كل عملية، وهي مشابهة جدًا في طبيعتها لما سيعطيك القفل على أي حال، هو سبب عدم رؤيتك بشكل عام لتطبيقات Shared_ptr الآمنة لمؤشر الترابط.إذا كنت بحاجة إلى شيء من هذا القبيل، فإنني أوصي بتغليف كائن المزامنة (mutex) وshared_ptr في فئة ملائمة لأتمتة القفل.

نصائح أخرى

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

يكرر:التعليق - السبب وراء عدم الحذف المزدوج للمكونات الإضافية هو أنها لا يتم حذفها على الإطلاق (وتنفيذ Boost::shared_ptr الذي أستخدمه لن يتم الحذف المزدوج، لأنه يستخدم زيادة وتناقصًا ذريًا خاصًا، لذا فهو سيتم حذفه مرة واحدة فقط، ولكن بعد ذلك يمكن أن تحتوي النتيجة على المؤشر من أحدهما وعدد المرجع من الآخر.أو إلى حد كبير أي مزيج من الاثنين.سيكون سيئا.).العبارة الواردة في مستندات التعزيز صحيحة كما هي، وستحصل على نفس الضمانات التي تحصل عليها مع البرنامج المدمج.

يكرر:EDIT2 - يختلف الموقف الأول الذي تصفه تمامًا بين استخدام العناصر المضمنة وshared_ptrs.في واحد (XCHG والحذف اليدوي) لا يوجد عدد مرجعي؛أنت تفترض أنك المالك الوحيد عندما تفعل ذلك.إذا كنت تستخدم مؤشرات مشتركة، فأنت تقول أن سلاسل الرسائل الأخرى قد تكون لها ملكية، مما يجعل الأمور أكثر تعقيدًا.أعتقد أن ذلك ممكن من خلال المقارنة والمبادلة، لكن هذا سيكون غير قابل للنقل على الإطلاق.

يأتي C++0x مع مكتبة ذرية، والتي من شأنها أن تسهل كتابة تعليمات برمجية عامة متعددة الخيوط.ربما سيتعين عليك الانتظار حتى يظهر ذلك لرؤية تطبيقات مرجعية جيدة عبر الأنظمة الأساسية للمؤشرات الذكية الآمنة للخيط.

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

هذا ليس نقدا -- قد تكون هناك شرعية في حالة استخدام, أنا فقط لا يمكن التفكير في الأمر.واسمحوا لي أن أعرف!

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

يمكنك استخدام هذا التنفيذ مؤشرات العد المرجعية الذرية على الأقل تنفيذ آلية العد المرجعي.

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

يمكنك القيام بذلك بسهولة عن طريق تضمين كائن كائن المزامنة (mutex) مع كل مؤشر مشترك، ولف أوامر الزيادة/النقصان بالقفل.

لا أعتقد أن هذا الأمر بهذه السهولة، فهو لا يكفي أن تقوم بتغليف فئات sh_ptr الخاصة بك باستخدام ملف CS.صحيح أنه إذا احتفظت بـ CS واحد لجميع المؤشرات المشتركة، فيمكن ضمان تجنب الوصول المتبادل وحذف كائنات sh_ptr بين سلاسل الرسائل المختلفة.ولكن هذا سيكون أمرًا فظيعًا، فكائن CS واحد لكل مؤشر مشترك سيكون بمثابة عنق الزجاجة الحقيقي.سيكون مناسبًا إذا كانت كل ptr -s جديدة قابلة للالتفاف تحتوي على CS مختلفة ولكن بهذه الطريقة يجب علينا إنشاء CS الخاص بنا ديناميكيًا، والتأكد من نسخ ctors لفئات sh_ptr لنقل هذه Cs المشتركة.والآن وصلنا لنفس المشكلة:من يضمن أن هذا Cs ptr قد تم حذفه بالفعل أم لا.يمكننا أن نكون أكثر ذكاءً مع إشارات m_bReleased المتطايرة لكل مثيل، لكن بهذه الطريقة لا يمكننا سد فجوات الأمان بين التحقق من العلامة واستخدام الأحرف المشتركة.لا أستطيع رؤية حل آمن تمامًا لهذه المشكلة.ربما تكون تلك Cs العالمية الرهيبة سيئة للغاية مثل قتل التطبيق.(اسف للغتى الانجليزيه)

قد لا يكون هذا بالضبط ما تريده، ولكن boost::atomic توفر الوثائق مثالاً على كيفية استخدام العداد الذري مع intrusive_ptr. intrusive_ptr هو أحد مؤشرات Boost الذكية، فهو يقوم "بالعد المرجعي المتطفل"، مما يعني أن العداد "مضمن" في الهدف بدلاً من توفيره بواسطة المؤشر الذكي.

يعزز atomic أمثلة الاستخدام:

http://www.boost.org/doc/html/atomic/usage_examples.html

في رأيي، الحل الأسهل هو استخدام intrusive_ptr مع بعض التعديلات الطفيفة (ولكنها ضرورية).

لقد شاركت تنفيذي أدناه:

http://www.philten.com/boost-smartptr-mt/

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