2012-01-11 11 views
30

सी ++ में 11 नए रैंडम नंबर जनरेटर इंजन और वितरण कार्यों का एक गुच्छा है। क्या वे धागे सुरक्षित हैं? यदि आप एकाधिक धागे के बीच एक एकल यादृच्छिक वितरण और इंजन साझा करते हैं, तो यह सुरक्षित है और क्या आपको अभी भी यादृच्छिक संख्याएं मिलेंगी? परिदृश्य मैं देख रहा हूँ की तरह कुछ,सी ++ 11 यादृच्छिक संख्या जनरेटर की थ्रेड सुरक्षा

void foo() { 
    std::mt19937_64 engine(static_cast<uint64_t> (system_clock::to_time_t(system_clock::now()))); 
    std::uniform_real_distribution<double> zeroToOne(0.0, 1.0); 
#pragma omp parallel for 
    for (int i = 0; i < 1000; i++) { 
     double a = zeroToOne(engine); 
    } 
} 

OpenMP या

void foo() { 
    std::mt19937_64 engine(static_cast<uint64_t> (system_clock::to_time_t(system_clock::now()))); 
    std::uniform_real_distribution<double> zeroToOne(0.0, 1.0); 
    dispatch_apply(1000, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(size_t i) { 
     double a = zeroToOne(engine); 
    }); 
} 

का उपयोग कर libdispatch का उपयोग कर रहा है।

उत्तर

25

सी ++ 11 मानक पुस्तकालय मोटे तौर पर सुरक्षित थ्रेड है। पीआरएनजी वस्तुओं पर थ्रेड सुरक्षा गारंटी कंटेनरों पर समान होती है। अधिक विशेष रूप से, चूंकि पीआरएनजी कक्षाएं सभी छद्म-यादृच्छिक हैं, यानी वे एक निश्चित वर्तमान स्थिति के आधार पर एक निर्धारिक अनुक्रम उत्पन्न करते हैं, वास्तव में निहित राज्य के बाहर किसी भी चीज़ पर झुकने या पोक करने के लिए कोई जगह नहीं है (जो भी दिखाई देता है उपभोक्ता)।

जैसे कंटेनरों को उन्हें साझा करने के लिए सुरक्षित बनाने के लिए ताले की आवश्यकता होती है, तो आपको PRNG ऑब्जेक्ट को लॉक करना होगा। यह धीमा और nondeterministic बना देगा। प्रति थ्रेड एक ऑब्जेक्ट बेहतर होगा।

§17.6.5.9 [res.on.data.races]:

1 इस अनुभाग को निर्दिष्ट आवश्यकताओं कि कार्यान्वयन डेटा दौड़ को रोकने के लिए पूरा करेगा (1.10)। प्रत्येक मानक लाइब्रेरी फ़ंक्शन प्रत्येक आवश्यकता को पूरा करने तक प्रत्येक आवश्यकता को पूरा करेगा। कार्यान्वयन नीचे उल्लिखित मामलों के अलावा अन्य मामलों में डेटा दौड़ को रोक सकता है।

2 एक C++ मानक पुस्तकालय समारोह करेगा नहीं प्रत्यक्ष या परोक्ष रूप पहुँच वस्तुओं (1.10) वर्तमान धागा जब तक वस्तुओं इस सहित समारोह के तर्क बयान, के माध्यम से प्रत्यक्ष या परोक्ष रूप एक्सेस किया जाता है के अलावा अन्य सूत्र द्वारा पहुँचा जा सकता।

3 एक C++ मानक पुस्तकालय समारोह प्रत्यक्ष या परोक्ष रूप वस्तुओं (1.10) वर्तमान धागा जब तक वस्तुओं इस सहित समारोह के गैर- स्थिरांक तर्क, के माध्यम से प्रत्यक्ष या परोक्ष रूप एक्सेस किया जाता है के अलावा अन्य धागे से सुलभ नहीं संशोधन नहीं करेगा।

4 [ध्यान दें: इसका मतलब यह है, उदाहरण के लिए, कि कार्यान्वयन तुल्यकालन के बिना आंतरिक प्रयोजनों के लिए एक स्थिर वस्तु उपयोग नहीं कर सकते क्योंकि यह एक डेटा दौड़ भी प्रोग्राम हैं जो स्पष्ट रूप से हिस्सा नहीं है वस्तुओं betweenthreads में हो सकता है। -endnote]

5 एक C++ मानक पुस्तकालय समारोह उन कंटेनर तत्वों पर अपने विनिर्देशन द्वारा आवश्यक कार्यों लागू के अलावा वस्तुओं परोक्ष रूप से अपने तर्कों के माध्यम से या अपने कंटेनर तर्क के तत्वों के माध्यम से सुलभ का उपयोग नहीं करेगा।

6 मानक लाइब्रेरी कंटेनर या स्ट्रिंग सदस्य फ़ंक्शन को कॉल करके प्राप्तकर्ताओं पर संचालन अंतर्निहित कंटेनर तक पहुंच सकता है, लेकिन इसे संशोधित नहीं करेगा। [नोट: विशेष रूप से, कंटेनर ऑपरेशंस जो उस कंटेनर से जुड़े इटरेटर पर ऑपरेशन के साथ इटरेटर्स को अमान्य करते हैं। - अंत नोट]

7 कार्यान्वयन उपयोगकर्ताओं को दिखाई नहीं दे रहे हैं और दौड़ के विरुद्ध संरक्षित हैं तो कार्यान्वयन धागे के बीच अपनी आंतरिक वस्तुओं को साझा कर सकते हैं।

8 जब तक अन्यथा उल्लिखित, सी ++ मानक पुस्तकालय कार्यों सभी कार्यों केवल वर्तमान धागा भीतर अगर उन संचालन उपयोगकर्ताओं के प्रभाव है कि दृश्य (1.10) हैं प्रदर्शन करेगा।

9 [नोट: पर कोई दृश्यमान दुष्प्रभाव नहीं होने पर यह संचालन समानांतर करने के लिए कार्यान्वयन की अनुमति देता है। - अंत नोट]

+0

यह मूल रूप से मुझे लगा कि यह थ्रेड-सुरक्षित नहीं था। क्या वितरण ऑब्जेक्ट 'std :: uniform_real_distribution शून्यToOne (0.0, 1.0) 'राशि थ्रेड साझा करना और प्रति थ्रेड एक इंजन का उपयोग करना ठीक है? – user1139069

+0

@ user1139069: नहीं, सुरक्षित नहीं। यद्यपि पहली नज़र में एक वितरण ऑब्जेक्ट * आंतरिक स्थिति को बनाए रखने के बिना इंजन ऑब्जेक्ट को बस प्रत्येक कॉल को प्रतिनिधि द्वारा अपना काम कर सकता है, अगर आप इसके बारे में सोचते हैं कि एक इंजन जो पर्याप्त यादृच्छिक बिट्स नहीं बना सकता है उसे दो बार बुलाया जा सकता है। लेकिन दो बार (या एक बार) अधिक हो सकता है, इसलिए अतिरिक्त यादृच्छिक बिट्स के कैशिंग को अनुमति देना बेहतर हो सकता है। §26.5.1.6 \t "यादृच्छिक संख्या वितरण आवश्यकताओं" इसकी अनुमति देता है; वितरण वस्तुओं विशेष रूप से राज्य है कि प्रत्येक कॉल के साथ परिवर्तन। इसलिए उन्हें लॉकिंग उद्देश्यों के लिए इंजन के हिस्से के रूप में माना जाना चाहिए। – Potatoswatter

0

documentation धागा सुरक्षा के कोई जिक्र नहीं है, इसलिए मुझे लगता है कि वे नहीं धागा सुरक्षित हैं।

+12

cppreference.com पर उल्लिखित नहीं किया गया है, ऐसा नहीं करता है। – Potatoswatter

2

मानक (अच्छी तरह से N3242) यादृच्छिक संख्या पीढ़ी दौड़ मुक्त होने का कोई उल्लेख नहीं करता है (सिवाय इसके कि rand नहीं है), इसलिए यह नहीं है (जब तक कि मुझे कुछ याद नहीं आया)। इसके अलावा उन्हें थ्रेडसेव रखने में वास्तव में कोई बात नहीं है, क्योंकि यह वास्तव में कुछ भी जीतने के बिना, अपेक्षाकृत भारी ऊपरी (संख्याओं की पीढ़ी की तुलना में कम से कम) की तुलना में होगा।

इसके अलावा मुझे वास्तव में एक लाभ दिखाई नहीं देता है और एक प्रति थ्रेड होने के बजाए एक साझा यादृच्छिक संख्या जेनरेटर होता है, प्रत्येक को थोड़ा अलग रूप से प्रारंभ किया जाता है (उदाहरण के लिए किसी अन्य जनरेटर के परिणाम से, या वर्तमान थ्रेड आईडी)। आखिरकार आप शायद जेनरेटर पर भरोसा न करें, प्रत्येक रन को एक निश्चित अनुक्रम उत्पन्न करना। इसलिए मैं कुछ इस तरह के रूप में अपने कोड को फिर से लिखने के लिए (openmp के लिए, के बारे में कोई सुराग नहीं libdispatch):

void foo() { 
    #pragma omp parallel 
    { 
    //just an example, not sure if that is a good way too seed the generation 
    //but the principle should be clear 
    std::mt19937_64 engine((omp_get_thread_num() + 1) * static_cast<uint64_t>(system_clock::to_time_t(system_clock::now()))); 
    std::uniform_real_distribution<double> zeroToOne(0.0, 1.0); 
    #pragma omp for 
     for (int i = 0; i < 1000; i++) { 
      double a = zeroToOne(engine); 
     } 
    } 
} 
+1

दरअसल, यदि एक ही आरएनजी विभिन्न धागे से पढ़ा जाता है, तो आप * निश्चित बीज के लिए यादृच्छिक संख्याओं की एक ही श्रृंखला प्राप्त करने पर भरोसा नहीं कर सकते हैं क्योंकि शेड्यूलिंग अलग-अलग धागे से अलग-अलग धागे से आरएनजी तक पहुंच का एक अलग क्रम का कारण बन सकती है । तो * विशेष रूप से * यदि आपको यादृच्छिक संख्या अनुक्रमों की प्रतिलिपि बनाने की आवश्यकता है, तो आपको धागे के बीच आरएनजी साझा नहीं करना चाहिए। – celtschk

+0

@celtschk: यह इस बात पर निर्भर करता है कि कैसे एक ही अनुक्रम प्राप्त करता है। मैं कहूंगा कि एक ही अनुक्रम (ग्लोबली) प्राप्त करेगा, यह सिर्फ इतना है कि थ्रेड प्रत्येक रन के साथ इसके विभिन्न हिस्सों को देखेंगे। – Grizzly