سؤال

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

من المستندات ، أفهم أن هذا يمكن إنجازه في g++ مع:

// global
Data *rt_data;

Data *swap_data(Data *new_data)
{
#ifdef __GNUC__
    // Atomic pointer swap.
    Data *old_d = __sync_lock_test_and_set(&rt_data, new_data);
#else
    // Non-atomic, cross your fingers.                                          
    Data *old_d = rt_data;
    rt_data = new_data;
#endif
    return old_d;
}

هذا هو المكان الوحيد في البرنامج (بخلاف الإعداد الأولي) حيث rt_data تم تعديله. متى rt_data يستخدم في سياق الوقت الحقيقي ، يتم نسخه إلى مؤشر محلي. ل old_d, ، في وقت لاحق عندما يكون من المؤكد أنه لا يتم استخدام الذاكرة القديمة ، سيتم تحريرها في الخيط غير RT. هل هذا صحيح؟ هل أحتاج volatile في أى مكان؟ هل هناك بدايات التزامن أخرى يجب أن أتصل بها؟

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

شكرا في وقت مبكر.

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

المحلول

عموما لا تستخدم volatile عند كتابة الرمز المتزامن في C/C++. دلالات volatile قريبة جدًا مما تريده حتى أنه مغري ولكن في النهاية متقلبة ليس كافي. للأسف Java/C# volatile != C/C++ volatile. Herb Sutter لديه عظيم مقالة - سلعة شرح الفوضى المربكة.

ما تريده حقًا هو سياج ذاكرة. __sync_lock_test_and_set يوفر المبارزة لك.

ستحتاج أيضًا إلى سياج ذاكرة عند نسخ (تحميل) مؤشر RT_DATA إلى نسختك المحلية.

قفل البرمجة المجانية صعبة. إذا كنت على استعداد لاستخدام ملحقات C ++ 0x من GCC ، فسيكون الأمر أسهل قليلاً:

#include <cstdatomic>

std::atomic<Data*> rt_data;

Data* swap_data( Data* new_data )
{
   Data* old_data = rt_data.exchange(new_data);
   assert( old_data != new_data );
   return old_data;
}

void use_data( )
{
   Data* local = rt_data.load();
   /* ... */
}

نصائح أخرى

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

الإجابة الأصلية:

نعم ، أنت بحاجة volatile على الخاص بك rt_data تصريح؛ في أي وقت يمكن تعديل متغير خارج تدفق التحكم في خيط الوصول إليه ، يجب إعلانه volatile. بينما قد تكون قادرًا على الفرار بدون volatile بما أنك تناسخ إلى مؤشر محلي ، volatile على الأقل يساعد في الوثائق ويمنع أيضًا بعض تحسينات البرامج التحويم التي يمكن أن تسبب مشاكل. النظر في المثال التالي ، المعتمد من DDJ:

volatile int a;
int b;
a = 1;
b = a;

إذا كان ذلك ممكنًا a لتغيير قيمتها بين a=1 و b=a, ، من ثم a يجب إعلانه volatile (ما لم يكن ، بالطبع ، تعيين قيمة خارج تاريخ b مقبولة). Multithreading ، وخاصة مع البدائية الذرية ، تشكل مثل هذا الموقف. يتم تشغيل الموقف أيضًا مع المتغيرات التي تم تعديلها بواسطة معالجات الإشارات والمتغيرات المعينة إلى مواقع الذاكرة الفردية (مثل سجلات I/O للأجهزة). أنظر أيضا هذا السؤال.

خلاف ذلك ، يبدو الأمر جيدًا بالنسبة لي.

في C ، ربما أستخدم البدائل الذرية التي توفرها جيب لهذا. سوف يستخدمون عملية ذرية حيثما كان ذلك متاحًا ويعودون إلى التنفيذ البطيء غير الصحيح القائم على Mutex إذا لم تكن العمليات الذرية متوفرة. قد يوفر Boost شيئًا مشابهًا لـ C ++.

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