2009-10-20 15 views
24

मैं लिनक्स पर चल रहे एक बहुप्रचारित प्रोग्राम (जी ++ 4.3 के साथ संकलित) विकसित कर रहा हूं और यदि आप थोड़ी देर के लिए खोज करते हैं तो आपको std :: string के बारे में बहुत डरावनी कहानियां मिलती हैं जो जीसीसी के साथ थ्रेड-सुरक्षित नहीं है। यह माना जाता है कि आंतरिक रूप से यह कॉपी-ऑन-राइट का उपयोग करता है जो हेल्ग्रिंड जैसे टूल के साथ कहर बरकरार रखता है।क्या gcc 4.3 के साथ std :: string thead-safe है?

मैंने एक छोटा प्रोग्राम बनाया है जो एक स्ट्रिंग को दूसरी स्ट्रिंग में कॉपी करता है और यदि आप दोनों तारों का निरीक्षण करते हैं तो वे दोनों एक ही आंतरिक _M_p पॉइंटर साझा करते हैं। जब एक स्ट्रिंग को संशोधित किया जाता है तो पॉइंटर बदल जाता है ताकि कॉपी-ऑन-राइट सामान ठीक काम कर रहा हो।

मैं क्या चिंतित हूं कि क्या होता है यदि मैं दो धागे के बीच एक स्ट्रिंग साझा करता हूं (उदाहरण के लिए इसे दो धागे के बीच थ्रेडसेफ डेटाक्यू में ऑब्जेक्ट के रूप में पास करना)। मैंने पहले से ही '-pthread' विकल्प के साथ संकलन करने का प्रयास किया है लेकिन ऐसा लगता है कि यह बहुत अंतर नहीं करता है। तो मेरे सवाल:

  • वहाँ threadsafe होने के लिए std :: स्ट्रिंग के लिए मजबूर करने कोई तरीका है? अगर यह प्राप्त करने के लिए कॉपी-ऑन-राइट व्यवहार अक्षम किया गया तो मुझे कोई फर्क नहीं पड़ता।
  • अन्य लोगों ने इसे कैसे हल किया है? या मैं पागल हो रहा हूँ?
..

मैं एक निश्चित जवाब खोजने के लिए तो मुझे आशा है कि आप लोग मेरी मदद कर सकते नहीं कर पा रहे

संपादित करें:

वाह, यह इस तरह के एक संक्षेप में जवाब की एक पूरी बहुत कुछ है पहर। धन्यवाद! जब मैं गाय को अक्षम करना चाहता हूं तो मैं निश्चित रूप से जैक के समाधान का उपयोग करूंगा। लेकिन अब मुख्य प्रश्न बन गया है: क्या मुझे वास्तव में गाय को अक्षम करना है? या गाय थ्रेड के लिए 'बुककीपिंग' सुरक्षित है? मैं वर्तमान में libstdC++ सूत्रों ब्राउज़ कर रहा हूँ, लेकिन यह है कि पिछले कुछ समय से लेने के लिए यह पता लगाने जा रहा है ...

संपादित 2

ठीक libstdC++ स्रोत कोड ब्राउज़ की हैं और मैं libstd में कुछ इस तरह लगता है ++ - v3 /include/bits/basic_string.h:

_CharT* 
    _M_refcopy() throw() 
    { 
#ifndef _GLIBCXX_FULLY_DYNAMIC_STRING 
    if (__builtin_expect(this != &_S_empty_rep(), false)) 
#endif 
      __gnu_cxx::__atomic_add_dispatch(&this->_M_refcount, 1); 
    return _M_refdata(); 
    } // XXX MT 

तो वहाँ निश्चित रूप से संदर्भ काउंटर करने के लिए परमाणु परिवर्तन के बारे में कुछ निष्कर्ष 01 है ...

मैं विक्रेता के टिप्पणी को यहां जवाब के रूप में चिह्नित कर रहा हूं क्योंकि मुझे लगता है कि हम इस निष्कर्ष पर पहुंचे हैं कि यह क्षेत्र अभी भी अनसुलझा है। गाय व्यवहार को रोकने के लिए मैं जैक लॉयड के जवाब का सुझाव दूंगा। एक दिलचस्प चर्चा के लिए सभी को धन्यवाद!

+0

+1 अच्छा सवाल! दुर्भाग्यवश, लोग सिर्फ "थ्रेडसेफ" पढ़ते हैं और सोचते हैं "नहीं!"। वे पूरे सवाल को बेहतर ढंग से पढ़ते हैं! :) – sellibitze

+0

चूंकि std :: string टेम्पलेट std :: basic_string का एक उदाहरण है, इसलिए स्रोत कोड पर एक नज़र रखना संभव हो सकता है। थ्रेड-सुरक्षा चालू/बंद करने वाले किसी भी मैक्रो को देखने का प्रयास करें। –

+0

वैसे, बहु-थ्रेडेड वातावरण में कॉपी-ऑन-राइट धीमा है, आपको * इसका उपयोग नहीं करना चाहिए, इसके लिए तैयार नहीं होना चाहिए। – GManNickG

उत्तर

15

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

सी ++ 0x ड्राफ्ट (एन 2 9 60) में "डेटा रेस टावरेंस" अनुभाग शामिल है जो मूल रूप से कहता है कि लाइब्रेरी घटक उपयोगकर्ता से छिपा हुआ साझा डेटा एक्सेस कर सकते हैं यदि केवल और यदि यह सक्रिय डेटा दौड़ से सक्रिय रूप से बचाता है। ऐसा लगता है कि std :: basic_string की कॉपी-ऑन-राइट कार्यान्वयन सुरक्षित w.r.t. के रूप में होना चाहिए। बहु-थ्रेडिंग एक अन्य कार्यान्वयन के रूप में जहां आंतरिक डेटा को विभिन्न स्ट्रिंग उदाहरणों के बीच कभी साझा नहीं किया जाता है।

मुझे 100% निश्चित नहीं है कि libstdC++ पहले से ही इसका ख्याल रखता है या नहीं। मुझे लगता है कि यह करता है। यह सुनिश्चित हो, the documentation

+1

उस विस्तृत उत्तर के लिए भी आपको बहुत बहुत धन्यवाद। मैं आपके द्वारा लिंक किए गए पृष्ठ की जांच करता हूं लेकिन यह मेरी राय में थोड़ा अस्पष्ट रहता है, सामान्य रूप से कंटेनरों के बारे में बात करता है (आपको पर्याप्त लॉकिंग प्रदान करनी चाहिए) और तारों के बारे में बहुत कुछ नहीं। :) डेटा रेस टावर कम से कम एक "बहाना" जैसा लगता है कि आगे बढ़ने के लिए और मान लें कि सबकुछ थ्रेड के बीच ठीक रहेगा क्योंकि यह लाइब्रेरी को लागू करने वाले लोगों पर ज़िम्मेदारी रखता है (बशर्ते कि मैं प्रोग्रामर पास के रूप में संदर्भ के बजाय मूल्य से तार) ... – Benjamin

0

कोई एसटीएल कंटेनर थ्रेड सुरक्षित नहीं है। इस तरह, पुस्तकालय का एक सामान्य उद्देश्य है (दोनों को एकल थ्रेडिंग मोड, या बहु थ्रेडिंग मोड में उपयोग किया जाना चाहिए)। मल्टीथ्रेडिंग में, आपको सिंक्रनाइज़ेशन तंत्र जोड़ना होगा।

+3

हालांकि स्ट्रिंग के साथ अंतर यह है कि मूल्य से इसे पास करने से थ्रेडिंग चिंताओं को प्रेरित किया जाता है - यह हो सकता है कम से कम कहने के लिए, कुछ हद तक अप्रत्याशित। –

+1

ऐसे त्वरित उत्तरों के लिए धन्यवाद दोस्तों!मुझे नहीं पता कि कोई एसटीएल कंटेनर थ्रेडसेफ नहीं है (और जब मुझे थ्रेडसेफ होने की आवश्यकता होती है तो लॉकिंग रैपर का सही ढंग से उपयोग करें) लेकिन जहां तक ​​मुझे पता है कि std :: स्ट्रिंग केवल "गुप्त रूप से" समान तारों के लिए उसी डेटास्टोर का उपयोग करने वाला है। मेरी चिंता यह है कि कॉपी-ऑन-राइट बुककीपिंग बिल्कुल थ्रेड सुरक्षित नहीं है, क्या मैं वहां सही हूं? – Benjamin

+9

मुझे समझ में नहीं आता कि यह इतने सारे अपवॉट क्यों प्राप्त करता है। दोस्तों, कृपया वास्तविक प्रश्न – sellibitze

11

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

#include <string> 
#include <cstdio> 

int main() 
    { 
    std::string orig = "I'm the original!"; 
    std::string copy_cow = orig; 
    std::string copy_mem = orig.c_str(); 
    std::printf("%p %p %p\n", orig.data(), 
          copy_cow.data(), 
          copy_mem.data()); 
    } 

दिखाएगा कि दूसरी प्रति (c_str का उपयोग करके) गाय को रोकती है।(क्योंकि std :: स्ट्रिंग केवल एक नंगे कॉन्स char * देखती है, और यह नहीं पता कि यह कहां से आया है या उसका जीवनकाल क्या हो सकता है, इसलिए इसे एक नई निजी प्रतिलिपि बनाना है)।

+12

माइनर चेतावनी पर ध्यान दें: c_str() के परिणाम को असाइन करने से स्ट्रिंग को छोटा कर दिया जाएगा यदि मूल में एम्बेडेड नल हैं। असाइन विधि (या कन्स्ट्रक्टर) का उपयोग करना एक कॉन्स char * और size_type लेना और उस पर "orig.data()" और "orig.size()" को पास करना सुरक्षित होगा। –

+0

यदि मैं 'अक्षम गाय' मार्ग के लिए जाने का निर्णय लेता हूं, तो मैं निश्चित रूप से इसका उपयोग करूंगा (और एरिक से जोड़ा गया टिप्पणी)। अच्छी तरह से किया :) – Benjamin

+0

@ एरिक उत्कृष्ट बिंदु, मुझे विश्वास नहीं है कि मैंने नहीं सोचा था कि यह कैसे नल के साथ बातचीत करेगा। धन्यवाद। –

0

की जाँच ऐसा लगता है कि यह कुछ समय पहले तय किया गया था: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=5444 (जो 3.1 में तय किया गया था http://gcc.gnu.org/bugzilla/show_bug.cgi?id=5432 की तुलना में एक ही मुद्दा है, के रूप में बंद कर दिया गया) गया था।

भी देखें libstdC++ internals के http://gcc.gnu.org/bugzilla/show_bug.cgi?id=6227

+0

मुझे लगता है कि बग iostreams कोड को संदर्भित करता है, स्ट्रिंग कोड नहीं? – Benjamin

+0

ओह, ठीक है। मैंने इसे संदर्भित किया क्योंकि मूल_स्ट्रिंग (# 5444) से संबंधित एक को 5432 की तुलना में एक ही रिज़ॉल्यूशन के रूप में बंद कर दिया गया था। मैंने इसे स्पष्ट करने के लिए अपना उत्तर संपादित किया। –

+0

धन्यवाद एरिक! जब तक हम बगट्रैक आइटम जोड़ रहे हों, यह भी संबंधित प्रतीत होता है: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=40518 – Benjamin

3

This अनुभाग राज्यों:

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

संदर्भ गणना को बहु-थ्रेडेड वातावरण में काम करना चाहिए। (जब तक कि आपका सिस्टम आवश्यक परमाणु प्रदान नहीं करता)

+0

यह अच्छा होगा अगर वास्तविक या स्टब फ़ंक्शंस का उपयोग किया गया हो तो यह बताने का एक त्वरित तरीका था। शायद मशीन कोड देखें और LOCK उपसर्ग की जांच करें ... –

0

this bug issue के अनुसार, std::basic_string की कॉपी-ऑन-राइट कार्यान्वयन अभी भी पूरी तरह से थ्रेड-सुरक्षित नहीं है। <ext/vstring.h> गाय के बिना एक कार्यान्वयन है और केवल पढ़ने के संदर्भ में बेहतर प्रदर्शन करता है।

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