क्या कूट सिंक्रनाइज़ / थ्रेड-सुरक्षित है?


112

सामान्य तौर पर मैं मानता हूं कि धाराएँ सिंक्रनाइज़ नहीं हैं, यह उचित लॉकिंग करने के लिए उपयोगकर्ता पर निर्भर है। हालांकि, coutमानक पुस्तकालय में विशेष उपचार जैसी चीजें मिलती हैं?

यही है, अगर कई सूत्र लिख रहे हैं तो coutक्या वे coutवस्तु को भ्रष्ट कर सकते हैं ? मुझे लगता है कि भले ही सिंक्रनाइज़ आपको अभी भी बेतरतीब ढंग से interleaved उत्पादन मिलेगा, लेकिन यह है कि interleaving की गारंटी है। यही है, यह coutकई धागे से उपयोग करने के लिए सुरक्षित है ?

क्या यह विक्रेता निर्भर है? Gcc क्या करता है?


महत्वपूर्ण : कृपया अपने उत्तर के लिए किसी प्रकार का संदर्भ प्रदान करें यदि आप "हां" कहते हैं क्योंकि मुझे इसके किसी प्रकार के प्रमाण की आवश्यकता है।

मेरी चिंता अंतर्निहित सिस्टम कॉल्स के बारे में भी नहीं है, वे ठीक हैं, लेकिन धाराएं शीर्ष पर बफरिंग की एक परत जोड़ देती हैं।


2
यह वेंडर आश्रित है। C ++ (C ++ 0x से पहले) में कई थ्रेड की कोई धारणा नहीं है।
स्वेन

2
C ++ 0x के बारे में क्या? यह एक मेमोरी मॉडल को परिभाषित करता है और एक धागा क्या है, इसलिए शायद ये चीजें आउटपुट में टपकती हैं?
रुबनेव

2
क्या कोई वेंडर है जो इसे धागा-सुरक्षित बनाता है?
edA-qa मोर्ट-ओरा-वाई

किसी के पास हालिया सी ++ 2011 प्रस्तावित मानक का लिंक है?
edA-qa मोर्ट-ओर-वाई-

4
कुछ अर्थों में यह जहां है printfचमकता है के रूप में पूरा उत्पादन के लिए लिखा है stdoutएक शॉट में; जब std::coutअभिव्यक्ति श्रृंखला के प्रत्येक लिंक का उपयोग करने के लिए अलग से उत्पादन किया जाएगा stdout; उनके बीच में कुछ अन्य थ्रेड राइटिंग stdoutहो सकती है जिसके कारण अंतिम आउटपुट का क्रम गड़बड़ हो जाता है।
लीजेंड्स 2k

जवाबों:


106

C ++ 03 मानक इसके बारे में कुछ नहीं कहता है। जब आपके पास किसी चीज़ की थ्रेड-सुरक्षा के बारे में कोई गारंटी नहीं है, तो आपको इसे थ्रेड-सुरक्षित नहीं मानना ​​चाहिए।

यहां विशेष रुचि इस तथ्य की है कि coutबफरिंग की जाती है। भले ही कॉल write(या जो कुछ भी यह है कि उस विशेष कार्यान्वयन में उस प्रभाव को पूरा करता है) पारस्परिक रूप से अनन्य होने की गारंटी है, बफर को विभिन्न थ्रेड द्वारा साझा किया जा सकता है। इससे धारा की आंतरिक स्थिति का भ्रष्टाचार जल्दी होगा।

और यहां तक ​​कि अगर बफर तक पहुंच को थ्रेड-सुरक्षित होने की गारंटी दी जाती है, तो आपको क्या लगता है कि इस कोड में क्या होगा?

// in one thread
cout << "The operation took " << result << " seconds.";

// in another thread
cout << "Hello world! Hello " << name << "!";

आप शायद चाहते हैं कि यहां प्रत्येक पंक्ति पारस्परिक बहिष्कार में कार्य करे। लेकिन कार्यान्वयन कैसे गारंटी दे सकता है?

C ++ 11 में, हमारे पास कुछ गारंटी है। FDIS §27.4.1 में निम्नलिखित कहता है [iostream.objects.overview]:

एक समकालिक (.527.5.3.4) समकालिक आईस्ट्रीम ऑब्जेक्ट के स्वरूपित और बिना सूचना वाले इनपुट (input27.7.2.1) और आउटपुट (.727.7.3.1) फ़ंक्शन या कई थ्रेड द्वारा एक मानक सी स्ट्रीम का उपयोग करने से डेटा रेस नहीं होगी (synchronized) 1.10)। [नोट: उपयोगकर्ताओं को अभी भी इन वस्तुओं और धाराओं के समकालिक उपयोग को कई थ्रेड द्वारा सिंक्रनाइज़ करना चाहिए यदि वे interleaved वर्णों से बचना चाहते हैं। - अंतिम नोट]

तो, आपको दूषित धाराएँ नहीं मिलेंगी, लेकिन यदि आप आउटपुट को कचरा नहीं बनना चाहते, तो भी आपको उन्हें मैन्युअल रूप से सिंक्रनाइज़ करने की आवश्यकता है।


2
तकनीकी रूप से C ++ 98 / C ++ 03 के लिए सही है, लेकिन मुझे लगता है कि हर कोई जानता है कि। लेकिन यह दो दिलचस्प सवालों का जवाब नहीं देता है: C ++ 0x के बारे में क्या? वास्तव में विशिष्ट कार्यान्वयन क्या करते हैं ?
निमो

1
@ edA-qa mort-ora-y: नहीं, आपके पास वह गलत है। सी ++ 11 स्पष्ट रूप से परिभाषित करता है कि मानक स्ट्रीम ऑब्जेक्ट्स को सिंक्रनाइज़ किया जा सकता है और अच्छी तरह से परिभाषित व्यवहार को बनाए रखा जा सकता है, न कि यह डिफ़ॉल्ट रूप से।
.िलजर्न

12
@ हिलजर्न - नहीं, @ edA-qa मोर्ट-ओर-वाई सही है। जब तक cout.sync_with_stdio()यह सच है, coutअतिरिक्त सिंक्रनाइज़ेशन के बिना कई थ्रेड्स से आउटपुट वर्णों का उपयोग करना अच्छी तरह से परिभाषित है, लेकिन केवल व्यक्तिगत बाइट्स के स्तर पर। इस प्रकार, विभिन्न थ्रेड्स में निष्पादित cout << "ab";और उदाहरण के लिए, cout << "cd"आउटपुट हो सकता है acdb, लेकिन अपरिभाषित व्यवहार का कारण नहीं हो सकता है।
JohannesD

4
@JohannesD: हम वहां समझौता कर रहे हैं - यह अंतर्निहित C API के साथ सिंक्रनाइज़ है। मेरा कहना है कि यह एक उपयोगी तरीके से "सिंक्रनाइज़" नहीं है, अर्थात यदि वे कचरा डेटा नहीं चाहते हैं, तो भी अभी भी मैन्युअल सिंक्रनाइज़ेशन की आवश्यकता है।
३५ पर ०१:१५ पर ildjarn

2
@ गिल्डरन, मैं कचरे के डेटा के साथ ठीक हूं, यह मुझे समझ में आता है। मुझे सिर्फ डेटा रेस की स्थिति में दिलचस्पी है, जो अब स्पष्ट होने लगती है।
edA-qa मोर्ट-ओर-वाई-

16

यह एक बड़ा सवाल है।

सबसे पहले, C ++ 98 / C ++ 03 में "थ्रेड" की कोई अवधारणा नहीं है। तो उस दुनिया में, प्रश्न अर्थहीन है।

C ++ 0x के बारे में क्या? मार्टिनो का जवाब देखें (जो मैं मानता हूं कि मुझे आश्चर्य हुआ)।

विशिष्ट कार्यान्वयन प्री-सी ++ 0x के बारे में कैसे? खैर, उदाहरण के लिए, यहाँ basic_streambuf<...>:sputcGCC 4.5.2 ("स्ट्रीमब्यूफ़" हेडर) के लिए स्रोत कोड है :

 int_type
 sputc(char_type __c)
 {
   int_type __ret;
   if (__builtin_expect(this->pptr() < this->epptr(), true)) {
       *this->pptr() = __c;
        this->pbump(1);
        __ret = traits_type::to_int_type(__c);
      }
    else
        __ret = this->overflow(traits_type::to_int_type(__c));
    return __ret;
 }

जाहिर है, यह कोई लॉकिंग नहीं करता है। और न ही करता है xsputn। और यह निश्चित रूप से स्ट्रीमब्यूफ का प्रकार है जिसका उपयोग कॉट उपयोग करता है।

जहाँ तक मैं बता सकता हूँ, libstdc ++ किसी भी स्ट्रीम ऑपरेशन के आसपास कोई लॉकिंग नहीं करता है। और मैं किसी भी तरह की उम्मीद नहीं करूंगा, क्योंकि यह धीमा होगा।

तो इस कार्यान्वयन के साथ, स्पष्ट रूप से दो धागे के आउटपुट के लिए एक दूसरे को भ्रष्ट करना संभव है ( केवल इंटरलेवेव)।

क्या यह कोड डेटा संरचना को ही दूषित कर सकता है? उत्तर इन कार्यों के संभावित इंटरैक्शन पर निर्भर करता है; उदाहरण के लिए, यदि एक थ्रेड बफर को फ्लश करने की कोशिश करता है, जबकि दूसरा कॉल करने की कोशिश करता है xsputnया जो भी हो। यह इस बात पर निर्भर करता है कि आपके कंपाइलर और सीपीयू मेमोरी लोड और स्टोर को फिर से कैसे तय करते हैं; यह सुनिश्चित करने के लिए एक सावधान विश्लेषण लगेगा। यह इस बात पर भी निर्भर करता है कि यदि दो थ्रेड्स एक ही स्थान को समवर्ती रूप से संशोधित करने का प्रयास करते हैं तो आपका सीपीयू क्या करता है।

दूसरे शब्दों में, भले ही यह आपके वर्तमान परिवेश में ठीक काम करने के लिए होता है, यह तब टूट सकता है जब आप अपने किसी रनटाइम, कंपाइलर या सीपीयू को अपडेट करते हैं।

कार्यकारी सारांश: "मैं नहीं"। एक लॉगिंग क्लास बनाएँ जो उचित लॉकिंग करता है, या C ++ 0x पर जाता है।

एक कमजोर विकल्प के रूप में, आप अटूट को सेट कर सकते हैं। यह संभावना है (हालांकि गारंटी नहीं है) जो बफर से संबंधित सभी तर्क को छोड़ देगा और writeसीधे कॉल करेगा । हालांकि यह निषेधात्मक रूप से धीमा हो सकता है।


1
अच्छा जवाब, लेकिन मार्टिनो की प्रतिक्रिया को देखें जो दर्शाता है कि C ++ 11 के लिए सिंक्रनाइज़ेशन को परिभाषित करता है cout
eda-qa mort-ora-y

7

C ++ मानक यह निर्दिष्ट नहीं करता है कि धाराओं को लिखना थ्रेड-सुरक्षित है, लेकिन आमतौर पर यह नहीं है।

www.techrepublic.com/article/use-stl-streams-for-easy-c-plus-plus-thread-safe-logging

और यह भी: क्या C ++ थ्रेड-सेफ (cout, cerr, clog) में मानक आउटपुट स्ट्रीम हैं?

अपडेट करें

इस बारे में नया मानक C ++ 11 क्या है, इसके बारे में जानने के लिए कृपया @Martinho फर्नांडिस के जवाब पर एक नज़र डालें।


3
मुझे लगता है कि C ++ 11 अब मानक है, यह उत्तर वास्तव में अब गलत है।
eda-qa mort-ora-y

6

जैसा कि अन्य उत्तर उल्लेख करते हैं, यह निश्चित रूप से विक्रेता-विशिष्ट है क्योंकि C ++ मानक थ्रेडिंग का कोई उल्लेख नहीं करता है (यह C ++ 0x में परिवर्तन)।

जीसीसी थ्रेड सुरक्षा और I / O के बारे में बहुत सारे वादे नहीं करता है। लेकिन यह वादा करता है के लिए प्रलेखन यहाँ है:

मुख्य सामान शायद है:

__Basic_file प्रकार, केवल सी stdio परत के चारों ओर छोटे रैपरों का एक संग्रह है (फिर से, संरचना के तहत लिंक देखें)। हम खुद को लॉक नहीं करते हैं, लेकिन बस कॉल करने के लिए फ़ोपेन, फ़ेराइट, और इसके आगे से गुजरते हैं।

इसलिए, 3.0 के लिए, "I / O के लिए सुरक्षित मल्टीथ्रेडिंग" के प्रश्न का उत्तर दिया जाना चाहिए, "I / O के लिए आपके प्लेटफ़ॉर्म का C लाइब्रेरी थ्रेडसेफ़ है?" कुछ डिफ़ॉल्ट रूप से हैं, कुछ नहीं हैं; कई सी लाइब्रेरी के कई कार्यान्वयन की पेशकश करते हैं, जिसमें थ्रेडसैफ़िटी और दक्षता के अलग-अलग ट्रेडऑफ़ हैं। आप, प्रोग्रामर, को हमेशा कई थ्रेड के साथ देखभाल करने की आवश्यकता होती है।

(एक उदाहरण के रूप में, POSIX मानक के लिए आवश्यक है कि C stdio FILE * ऑपरेशन परमाणु हैं। POSIX- अनुरूपण C लाइब्रेरीज़ (जैसे, Solaris और GNU / Linux पर) के पास File * s के लिए ऑपरेशन को व्यवस्थित करने के लिए एक आंतरिक म्यूटेक्स है। हालांकि, आपको अभी भी ज़रूरत है। एक थ्रेड में fclose (fs) को कॉल करने जैसी बेवकूफी नहीं करनी चाहिए।

इसलिए, यदि आपके प्लेटफ़ॉर्म की सी लाइब्रेरी थ्रेडसेफ़ है, तो आपका फ़ॉस्टस्ट I / O ऑपरेशंस सबसे निचले स्तर पर थ्रेडसेफ़ होगा। उच्च-स्तरीय परिचालनों के लिए, जैसे कि स्ट्रीम फ़ॉर्मेटिंग क्लासेस में निहित डेटा में हेरफेर करना (उदाहरण के लिए, एक std :: tostream के अंदर कॉलबैक सेट करना), आपको किसी अन्य महत्वपूर्ण साझा किए गए संसाधन की तरह ऐसे एक्सेस को संरक्षित करने की आवश्यकता है।

मुझे पता नहीं है कि कुछ भी बदल गया है साइन 3.0 समय सीमा का उल्लेख किया है।

MSVC के थ्रेड सुरक्षा दस्तावेज iostreamsयहां देखे जा सकते हैं: http://msdn.microsoft.com/en-us/library/c9ceah3b.aspx :

एक एकल ऑब्जेक्ट कई थ्रेड्स से पढ़ने के लिए थ्रेड सुरक्षित है। उदाहरण के लिए, एक वस्तु ए को देखते हुए, थ्रेड 1 से और थ्रेड 2 से एक साथ पढ़ना सुरक्षित है।

यदि किसी एक वस्तु को एक धागे से लिखा जा रहा है, तो सभी उस वस्तु को पढ़ते हैं और उसी या अन्य धागे पर लिखते हैं। उदाहरण के लिए, किसी वस्तु A को दिए जाने पर, यदि थ्रेड 1 A को लिख रहा है, तो थ्रेड 2 को पढ़ने या A से लिखने से रोका जाना चाहिए।

एक प्रकार के एक उदाहरण को पढ़ना और लिखना सुरक्षित है, भले ही दूसरा धागा एक ही प्रकार के विभिन्न उदाहरणों को पढ़ या लिख ​​रहा हो। उदाहरण के लिए, दिए गए ऑब्जेक्ट A और B एक ही प्रकार के हैं, यह सुरक्षित है यदि A को थ्रेड 1 में लिखा जा रहा है और B को थ्रेड 2 में पढ़ा जा रहा है।

...

आईस्ट्रीम क्लासेस

आईस्ट्रीम कक्षाएं एक अपवाद के साथ अन्य वर्गों के समान नियमों का पालन करती हैं। कई थ्रेड्स से ऑब्जेक्ट को लिखना सुरक्षित है। उदाहरण के लिए, थ्रेड 1 थ्रेड के रूप में एक ही समय में cout करने के लिए लिख सकता है। हालाँकि, इसके परिणामस्वरूप दो थ्रेड्स से इंटरमिक्स किया जा सकता है।

नोट: स्ट्रीम बफर से पढ़ना एक रीड ऑपरेशन नहीं माना जाता है। इसे एक लेखन ऑपरेशन के रूप में माना जाना चाहिए, क्योंकि इससे कक्षा की स्थिति बदल जाती है।

ध्यान दें कि जानकारी MSVC के नवीनतम संस्करण के लिए है (वर्तमान में VS 2010 / MSVC 10 / cl.exe16.x के लिए)। आप पृष्ठ पर एक ड्रॉपडाउन नियंत्रण का उपयोग करके MSVC के पुराने संस्करणों के लिए जानकारी का चयन कर सकते हैं (और जानकारी पुराने संस्करणों के लिए अलग है)।


1
"मुझे नहीं पता कि कुछ भी बदल गया है साइन 3.0 समय सीमा का उल्लेख किया है।" यह निश्चित रूप से किया। पिछले कई वर्षों से, जी ++ स्ट्रीम कार्यान्वयन ने अपनी बफरिंग का प्रदर्शन किया है।
निमो
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.