2012-01-25 18 views
7

क्या यह एक सामान्य परमाणु स्वैप फ़ंक्शन के लिए सही कार्यान्वयन है? मैं जीसीसी पर एक सी ++ 03-संगत समाधान की तलाश में हूं।जीसीसी परमाणु निर्मित का उपयोग कर परमाणु स्वैप समारोह

template<typename T> 
void atomic_swap(T & a, T & b) { 
    static_assert(sizeof(T) <= sizeof(void*), "Maximum size type exceeded."); 
    T * ptr = &a; 
    b =__sync_lock_test_and_set(ptr, b); 
    __sync_lock_release(&ptr); 
} 

यदि नहीं, तो इसे ठीक करने के लिए मुझे क्या करना चाहिए?

भी: __sync_lock_release हमेशा आवश्यक है? अन्य कोडबेस के माध्यम से खोज करते समय मैंने पाया कि इसे अक्सर नहीं कहा जाता है। रिहाई के बिना फोन मेरे कोड इस तरह दिखता है:

template<typename T> 
void atomic_swap(T & a, T & b) { 
    static_assert(sizeof(T) <= sizeof(void*), "Maximum size type exceeded."); 
    b = __sync_lock_test_and_set(&a, b); 
} 

पुनश्च: Atomic swap in GNU C++ एक ऐसी ही सवाल है, लेकिन यह मेरे सवाल का जवाब नहीं है क्योंकि प्रदान की जवाब सी ++ 11 के std::atomic और यह है हस्ताक्षर Data *swap_data(Data *new_data) जो नहीं करता है 'की आवश्यकता है टी swap फ़ंक्शन के लिए बिल्कुल समझ में आता है। (यह वास्तव में प्रदान किए गए तर्क को वैश्विक चर के साथ स्वैप करता है जिसे फ़ंक्शन से पहले परिभाषित किया गया था।)

+0

ऐसा लगता है कि 'ए' तक पहुंच केवल परमाणु माना जाता है? –

+0

एक पहिया का फिर से आविष्कार क्यों किया? Http://concurrencykit.org/ –

+0

@BenVoigt पर यह पोस्ट स्पष्ट उत्तर नहीं देता है। और यह एक वैश्विक चर के साथ तर्क swaps। – StackedCrooked

उत्तर

9

ध्यान रखें कि स्वैप का यह संस्करण पूरी तरह से परमाणु संचालन नहीं है। जबकि b का मूल्य a परमाणु रूप से कॉपी किया जाएगा, a का मान b के मान को अन्य थ्रेड द्वारा प्रतिलिपि में बदल सकता है। दूसरे शब्दों में b को असाइनमेंट अन्य धागे के संबंध में परमाणु नहीं है। इस प्रकार आप एक ऐसी स्थिति के साथ समाप्त हो सकते हैं जहां a == 1, और b == 2, और जीसीसी निर्मित होने के बाद, आप a == 2 के साथ समाप्त हो जाते हैं और 1 का मूल्य लौटाया जा रहा है, लेकिन अब एक और थ्रेड ने b से 3 का मान बदल दिया है, और आप b में 1 के मान के साथ उस मान को लिखें। इसलिए जब आप "तकनीकी रूप से" मानों को बदल सकते हैं, तो आपने इसे परमाणु रूप से नहीं किया ... एक और धागा b के मूल्य को जीसीसी परमाणु अंतर्निहित से वापसी के बीच, और उस वापसी मूल्य के असाइनमेंट के बीच छुआ b

lea RAX, qword ptr [RDI] // T * ptr = &a; 
mov RCX, qword ptr [RSI] // copy out the value referenced by b into a register 
xchg [RAX], RCX   // __sync_lock_test_and_set(&a, b) 
mov qword ptr [RSI], RCX // place the exchange value back into b (not atomic!!) 

ईमानदारी से कहूं तो, आप एक DCAS की तरह एक हार्डवेयर आपरेशन के बिना एक ताला मुक्त दो अलग-अलग मेमोरी स्थानों के परमाणु स्वैप नहीं कर सकते: से विधानसभा खड़े बिंदु को देखा, तो आप की तरह निम्नलिखित कुछ है या एक कमजोर लोड-लिंक्ड/स्टोर-सशर्त, या संभावित रूप से कुछ अन्य विधि जैसे ट्रांजैक्शनल मेमोरी (जो स्वयं ठीक-ठीक लॉकिंग का उपयोग करता है) का उपयोग कर रहा है।

दूसरा, जैसा कि आपका फ़ंक्शन अभी लिखा गया है, यदि आप चाहते हैं कि आपके परमाणु संचालन दोनों को प्राप्त करने और सेमेन्टिक्स जारी करने के लिए, तो हाँ, आपको या तो __sync_lock_release में स्थानांतरित करना होगा, या आप जा रहे हैं __sync_synchronize के माध्यम से एक पूर्ण मेमोरी बाधा जोड़ना होगा। अन्यथा यह केवल __sync_lock_test_and_set पर अर्थशास्त्र प्राप्त करेगा। फिर भी, यह परमाणु रूप से एक दूसरे के साथ दो अलग-अलग स्मृति स्थानों को स्वैप नहीं करता है ...

+0

मैं समझता हूं कि 'बी' के लिए लिखने से यह एक गैर-परमाणु संचालन करता है और इस प्रकार थ्रेड-सुरक्षित नहीं होता है। हालांकि, मुझे इसे सुरक्षित बनाने का कोई तरीका नहीं दिख रहा है। क्या इसका मतलब यह है कि पॉइंटर प्रकारों का लॉकलेस स्वैप वास्तव में संभव नहीं है? – StackedCrooked

+0

बहुत कुछ, जब तक कि हार्डवेयर डीसीएएस की तरह कुछ समर्थन नहीं करता है, या लोड-लिंक्ड/स्टोर सशर्त ऑपरेशन जो काफी कमजोर है, ताकि स्मृति में कोई पहुंच हो या कैश-लाइन पर कम से कम मेमोरी एक्सेस हो , और आपके द्वारा स्वैप किए जा रहे दो मान एक ही कैश-लाइन पर हैं – Jason

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