2010-10-25 8 views
9

मैं सी ++ 0x समर्थन के साथ प्रयोग कर रहा हूं और एक समस्या है, मुझे लगता है कि वहां नहीं होना चाहिए। या तो मैं इस विषय को समझ नहीं पा रहा हूं या जीसीसी में एक बग है।मेमोरी ऑर्डरिंग मुद्दे

मेरे पास निम्न कोड है, प्रारंभ में x और y बराबर हैं। थ्रेड 1 हमेशा पहले x बढ़ाता है और फिर y बढ़ाता है। दोनों परमाणु पूर्णांक मान हैं, इसलिए वृद्धि में कोई समस्या नहीं है। थ्रेड 2 जांच रहा है कि xy से कम है और यदि ऐसा है तो एक त्रुटि संदेश प्रदर्शित करता है।

यह कोड कभी-कभी विफल रहता है, लेकिन क्यों? यहां मुद्दा शायद मेमोरी रीडरिंग है, लेकिन सभी परमाणु संचालन अनुक्रमिक रूप से डिफ़ॉल्ट रूप से सुसंगत हैं और मैंने स्पष्ट रूप से उन किसी भी परिचालन में आराम नहीं किया है। मैं इस कोड को x86 पर संकलित कर रहा हूं, जहां तक ​​मुझे पता है कि कोई ऑर्डरिंग समस्या नहीं होनी चाहिए। क्या आप कृपया बता सकते हैं कि समस्या क्या है?

#include <iostream> 
#include <atomic> 
#include <thread> 

std::atomic_int x; 
std::atomic_int y; 

void f1() 
{ 
    while (true) 
    { 
     ++x; 
     ++y; 
    } 
} 

void f2() 
{ 
    while (true) 
    { 
     if (x < y) 
     { 
      std::cout << "error" << std::endl; 
     } 
    } 
} 

int main() 
{ 
    x = 0; 
    y = 0; 

    std::thread t1(f1); 
    std::thread t2(f2); 

    t1.join(); 
    t2.join(); 
} 

परिणाम here देखा जा सकता है।

+0

असल में यह सी ++ 0x का प्रयोगात्मक कार्यान्वयन है, इसलिए दूसरा संभव है, लेकिन मेरा मानना ​​है कि पहला व्यक्ति अधिक संभावना है: पी – confucius

+3

ऊपर पोस्ट किया गया कोड हमेशा "त्रुटि", ('x' हमेशा 'y' से अधिक या बराबर होगा) क्या आप यही चाहते थे? – Paul

+0

क्यों (x confucius

उत्तर

11

समस्या अपने परीक्षण में हो सकता है:

if (x < y) 

धागा x का मूल्यांकन कर सकता है और भी बहुत बाद में जब तक y का मूल्यांकन करने के लिए चारों ओर नहीं मिलता है।

+0

मुझे लगता है कि यह समस्या है, सवाल यह है कि वास्तव में अनुक्रमिक रूप से लगातार परमाणु संचालन इसे रोकना चाहिए, है ना? – confucius

+0

बहुत बहुत धन्यवाद! पी रोबलेम अभिव्यक्ति मूल्यांकन का अनिश्चित अनुक्रम है, इतना आसान :) – confucius

+4

@confucius: जबकि आपके परिदृश्य में ऑर्डर पर निर्भरता हो सकती है कि चर को पढ़ा जा सकता है, अधिक सामान्य मुद्दा यह है कि 2 अलग-अलग परमाणु उदाहरण पढ़ना परमाणु नहीं है। –

12

तुलना के साथ एक समस्या है:

x < y 

subexpressions के मूल्यांकन के आदेश (इस मामले में, x और y का) अनिर्दिष्ट है, इसलिए yx से पहले मूल्यांकन किया जा सकता या x मूल्यांकन किया जा सकता y से पहले।

x = 0; y = 0; 
t2 reads x (value = 0); 
t1 increments x; x = 1; 
t1 increments y; y = 1; 
t2 reads y (value = 1); 
t2 compares x < y as 0 < 1; test succeeds! 

आप स्पष्ट रूप से यह सुनिश्चित करें कि y पहले पढ़ने के लिए है, तो आप समस्या से बचने कर सकते हैं:

int yval = y; 
int xval = x; 
if (xval < yval) { /* ... */ } 
+0

धन्यवाद! बस। क्षमा करें दोस्तों, प्लस नहीं कर सकते हैं, कार्यालय में https अवरुद्ध है और मैं लॉगिन नहीं कर सकता :( – confucius

-3

पहले, मैं के साथ सहमत

तो x पहले पढ़ने के लिए है, तो आप एक समस्या है "माइकल बुर" और "जेम्स मैकनेलिस"। आपका परीक्षण उचित नहीं है, और असफल होने की वैध संभावना है। हालांकि अगर आप परीक्षण को फिर से लिखते हैं तो "जेम्स मैकनेलिस" का सुझाव है कि परीक्षण विफल हो सकता है।

इसका पहला कारण यह है कि आप volatile अर्थशास्त्र का उपयोग नहीं करते हैं, इसलिए संकलक आपके कोड के अनुकूलन कर सकता है (जिसे एकल-थ्रेडेड केस में ठीक होना चाहिए)।

लेकिन volatile के साथ भी आपके कोड को काम करने की गारंटी नहीं है।

मुझे लगता है कि आप मेमोरी रीडरिंग की अवधारणा को पूरी तरह से समझ नहीं पाते हैं। असल में मेमोरी रीड/राइट रीडर दो स्तरों पर हो सकता है:

  1. कंपाइलर जेनरेट किए गए पढ़ने/लिखने के निर्देशों के क्रम का आदान-प्रदान कर सकता है।
  2. सीपीयू मनमाने ढंग से क्रम में स्मृति पढ़ने/लिखने के निर्देश निष्पादित कर सकता है।

volatile का उपयोग (1) को रोकता है। हालांकि आपने रोकने के लिए कुछ भी नहीं किया है (2) - हार्डवेयर द्वारा मेमोरी एक्सेस रीडरिंग।

को रोकने के लिए यह आपके कोड में विशेष स्मृति बाड़ निर्देश रखना चाहिए (कि सीपीयू के लिए नामित कर रहे हैं, volatile जो केवल संकलक के लिए है के विपरीत)।

x86/x64 में कई अलग-अलग मेमोरी बाड़ निर्देश हैं। डिफ़ॉल्ट रूप से पूर्ण स्मृति बाड़ के मुद्दों के साथ lock अर्थशास्त्र के साथ हर निर्देश भी।

अधिक यहाँ जानकारी:

http://en.wikipedia.org/wiki/Memory_barrier

+2

सी ++ 0 एक्स परमाणु सही मेमोरी ऑर्डरिंग की गारंटी देते हैं। –

+1

वाल्दो - यहां अस्थिरता का उपयोग करने की आवश्यकता नहीं है, क्योंकि डिफ़ॉल्ट रूप से स्मृति द्वारा उत्पन्न स्मृति बाधाएं ++ 0x परमाणु संचालन दोनों (1) और (2) को रोकें। – confucius

4

हर अब और फिर, x आसपास 0 अभी y से पहले रैप करने के लिए चारों ओर शून्य करने के लिए लपेटो जाएगा। इस बिंदु पर y वैध रूप से x से अधिक होगा।

+2

इसे संपादित करने पर एक शॉट लिया। यह भी ध्यान दिया जा सकता है कि हस्ताक्षरित अतिप्रवाह अपरिभाषित व्यवहार की ओर जाता है, हालांकि अपेक्षित ओवरफ्लो लपेटें अपेक्षित हैं। – GManNickG

+0

सहमत हैं। ओवरफ़्लो नहीं था ऐसा होना चाहिए, यह सिर्फ परीक्षण के लिए किया गया था, लेकिन यह भी एक समस्या हो सकती है। धन्यवाद। – confucius

संबंधित मुद्दे