سؤال

أنا أهرب في موقف حيث أحتاج إلى المبلغ الذري لقيمتين في الذاكرة. الرمز الذي ورثته يذهب مثل هذا:

int a = *MemoryLocationOne;
memory_fence();
int b = *MemoryLocationTwo;
return (a + b) == 0;

إن القراءات الفردية من A و B هي ذرية، وكلها تكتب في أماكن أخرى من التعليمات البرمجية لهذين مواقع الذاكرة هي أيضا غير مقفل. ومع ذلك، فإن المشكلة هي أن قيم المواقعتين يمكن أن تتغير بين القراءتين.

إذن كيف أجعل هذه العملية ذرية؟ أعرف أن كل شيء عن CAS، لكنه يميل إلى إشراك فقط إجراء عمليات الكتابة بالقراءة والكتب الذرية وهذا ليس ما أريد القيام به هنا.

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

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

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

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

المحلول

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

مع هذا، يمكنك التأكد من أنه إذا كنت:

memory_fence_start();
int a = *MemoryLocationOne;
int b = *MemoryLocationTwo;
int test = (a + b) == 0;
memory_fence_stop();

return test;

ومن بعد a لن تتغير أثناء القراءة b. وبعد ولكن مرة أخرى، عليك استخدام نفس آلية المزامنة الكل الولوج إلى a و ل b.

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

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

نصائح أخرى

يجب عليك التأكد من أن كل مكان تم قراءة أي من القيمتين أو كتابته، كانوا محاذاين حاجز ذاكرة (قفل أو قسم حرج).

// all reads...
lock(lockProtectingAllAccessToMemoryOneAndTwo)
{
    a = *MemoryLocationOne;
    b = *MemoryLocationTwo;
}

...

// all writes...
lock(lockProtectingAllAccessToMemoryOneAndTwo)
{
    *MemoryLocationOne = someValue;
    *MemoryLocationTwo = someOtherValue;
}

إذا كنت تستهدف X86، فيمكنك استخدام دعم مقارنة / تبادل 64 بت وحزم كل من Int's في كلمة واحدة 64 بت.

على Windows، سوف تفعل هذا:

// Skipping ensuring padding.
union Data
{
     struct members
     {
         int a;
         int b;
     };

     LONGLONG _64bitData;  
};

Data* data;


Data captured;

do
{
    captured = *data;
    int result = captured.members.a + captured.members.b;
} while (InterlockedCompareExchange64((LONGLONG*)&data->_64bitData,
                    captured._64BitData,
                    captured._64bitData) != captured._64BitData);

حقا قبيحة. أود أن أقترح استخدام قفل - أكثر صحة بكثير.

تحرير: لتحديث وقراءة الأجزاء الفردية:

data->members.a = 0;
fence();

data->members.b = 0;
fence();

int captured = data->members.a;

int captured = data->members.b;

حقا ليست هناك طريقة للقيام بذلك دون قفل. لا توجد معالجات قراءة ذرية مزدوجة، بقدر ما أعرف.

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