mdx

2016-06-27 4 views
5

के साथ सम्मिलन लॉक करने के बावजूद std :: unordered_map के साथ डेटा रेस मेरे पास एक सी ++ 11 प्रोग्राम है जो कुछ कंप्यूटेशंस करता है और उन कंप्यूटेशंस के परिणामों को कैश करने के लिए std::unordered_map का उपयोग करता है। कार्यक्रम एकाधिक धागे का उपयोग करता है और वे कंप्यूटेशंस के परिणामों को संग्रहीत करने और साझा करने के लिए साझा unordered_map का उपयोग करते हैं।mdx

unordered_map और एसटीएल कंटेनर चश्मा के अपने पढ़ने के साथ-साथ unordered_map thread safety के आधार पर, ऐसा लगता है कि एक unordered_map, एक से अधिक थ्रेड द्वारा साझा, एक समय में एक धागा लेखन, लेकिन एक समय में कई पाठकों संभाल कर सकते हैं।

इसलिए, मैं std::mutex का उपयोग कर रहा हूं ताकि insert() मानचित्र पर कॉल को लपेट सके, ताकि एक समय में केवल एक ही थ्रेड डाला जा सके।

हालांकि, मेरे find() कॉल में मेरे पढ़ने से म्यूटेक्स नहीं है, ऐसा लगता है कि कई धागे एक बार में पढ़ने में सक्षम होना चाहिए। हालांकि, मुझे कभी-कभी डेटा रेस मिलती है (जैसा कि टीएसएएन द्वारा पता चला है), स्वयं को एसईजीवी में प्रकट कर रहा है। डेटा रेस स्पष्ट रूप से ऊपर वर्णित insert() और find() कॉल को इंगित करती है।

जब मैं find() कॉल को म्यूटेक्स में लपेटता हूं, तो समस्या दूर हो जाती है। हालांकि, मैं समवर्ती पढ़ने को क्रमबद्ध नहीं करना चाहता, क्योंकि मैं इस कार्यक्रम को जितनी जल्दी हो सके बनाने की कोशिश कर रहा हूं। (एफवाईआई: मैं जीसीसी 5.4 का उपयोग कर दौड़ रहा हूं।)

ऐसा क्यों हो रहा है? क्या std::unordered_map की समवर्ती गारंटी की मेरी समझ गलत है?

+7

ऐसा लगता है कि आपको 'shared_mutex' की आवश्यकता है क्योंकि सभी लिखने और पढ़ने के लिए कॉल को सिंक्रनाइज़ेशन की आवश्यकता होती है क्योंकि आपके पास लेखक है। – NathanOliver

+6

लिंक्ड थ्रेड में उत्तर यह कहता है: "* ए। एक ही समय में पढ़ने वाले एकाधिक थ्रेड, * ** या ** * बी। एक ही समय में एक थ्रेड लेखन *"। ** या ** ध्यान दें। – Pixelchemist

+6

आपने कल्पना का गलत व्याख्या किया है; यह एकाधिक पाठकों को अनुमति देता है, लेकिन एकल लेखक की परवाह किए बिना एकाधिक पाठक नहीं। आपको नाथनऑलिवर की टिप्पणी – antlersoft

उत्तर

3

आपको अपने पाठकों के लिए लेखकों को रखने के लिए अभी भी mutex की आवश्यकता है, लेकिन आपको साझा एक की आवश्यकता है।

using mutex_type = std::shared_timed_mutex; 
using read_only_lock = std::shared_lock<mutex_type>; 
using updatable_lock = std::unique_lock<mutex_type>; 

mutex_type mtx; 
std::unordered_map<int, std::string> m; 

// code to update map 
{ 
    updatable_lock lock(mtx); 

    m[1] = "one"; 
} 

// code to read from map 
{ 
    read_only_lock lock(mtx); 

    std::cout << m[1] << '\n'; 
} 
2

कि दृष्टिकोण के साथ कई समस्याएं हैं: C++14 एक std::shared_timed_mutex कि आप दायरे वाले ताले std::unique_lock और std::shared_lock इस तरह के साथ उपयोग कर सकते हैं।

पहले, std::unordered_map में find के दो अधिभार हैं - एक जो const है, और जो नहीं है।
मैं यह कहने की हिम्मत करता हूं कि मुझे विश्वास नहीं है कि find का गैर-कॉन्स संस्करण मानचित्र को म्यूटेट करेगा, लेकिन फिर भी एकाधिक थ्रेड से गैर कॉन्स विधि का आविष्कार करने वाले कंपाइलर के लिए डेटा रेस है और कुछ कंपाइलर्स वास्तव में अपरिभाषित व्यवहार का उपयोग करते हैं बुरा अनुकूलन के लिए।
तो पहली बात - आपको यह सुनिश्चित करने की ज़रूरत है कि जब कई धागे std::unordered_map::find का आह्वान करते हैं तो वे इसे कॉन्स्ट संस्करण के साथ करते हैं। जिसे मानचित्र को संदर्भित करके संदर्भित किया जा सकता है और फिर वहां से find का आह्वान किया जा सकता है।

दूसरा, आप उस भाग को याद करते हैं जो कई धागे आपके मानचित्र पर पाए जाते हैं, लेकिन अन्य धागे ऑब्जेक्ट पर गैर कॉन्स्ट विधि का आह्वान नहीं कर सकते हैं! मैं निश्चित रूप से कई थ्रेड्स को find पर कॉल कर सकता हूं और कुछ समय पर insert पर कॉल कर सकता हूं, जिससे डेटा रेस हो सकती है। कल्पना करें कि, उदाहरण के लिए, insert नक्शा का आंतरिक बफर फिर से स्थानांतरित करता है जबकि कुछ अन्य थ्रेड इसे वांछित जोड़ी ढूंढने के लिए पुन: सक्रिय करता है।

इसका समाधान सी ++ 14 shared_mutex का उपयोग करना है जिसमें एक विशेष/साझा लॉकिंग मोड है। जब थ्रेड कॉल find, यह साझा मोड पर लॉक को लॉक करता है, जब थ्रेड insert पर कॉल करता है तो यह इसे विशेष लॉक पर लॉक करता है।

यदि आपका कंपाइलर shared_mutex का समर्थन नहीं करता है, तो आप प्लेटफ़ॉर्म विशिष्ट सिंक्रनाइज़ेशन ऑब्जेक्ट्स का उपयोग कर सकते हैं, जैसे pthread_rwlock_t लिनक्स पर और SRWLock विंडोज पर।

एक और संभावना है कि इंटेल की थ्रेड-बिल्डिंग ब्लॉक लाइब्रेरी द्वारा प्रदान की गई लॉक-फ्री हैशैप का उपयोग करें, या एमएसवीसी कॉन्सुरेंसी रनटाइम पर concurrent_map। कार्यान्वयन स्वयं लॉक-फ्री एल्गोरिदम का उपयोग करता है जो सुनिश्चित करता है कि एक्सेस हमेशा थ्रेड-सुरक्षित और एक ही समय में तेज़ होता है।

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