क्या निम्न कार्यान्वयन, Singleton(मेयर्स सिंग्लटन) थ्रेड के आलसी इनिशियलाइज़ेशन का उपयोग कर सुरक्षित है?
static Singleton& instance()
{
static Singleton s;
return s;
}
यदि नहीं, तो इसे थ्रेड को सुरक्षित क्यों और कैसे बनाया जाए?
क्या निम्न कार्यान्वयन, Singleton(मेयर्स सिंग्लटन) थ्रेड के आलसी इनिशियलाइज़ेशन का उपयोग कर सुरक्षित है?
static Singleton& instance()
{
static Singleton s;
return s;
}
यदि नहीं, तो इसे थ्रेड को सुरक्षित क्यों और कैसे बनाया जाए?
जवाबों:
में सी ++ 11 , यह धागा सुरक्षित है। के अनुसार मानक , §6.7 [stmt.dcl] p4:
यदि नियंत्रण घोषणा को समवर्ती रूप से दर्ज करता है जबकि चर को प्रारंभ किया जा रहा है, तो समवर्ती निष्पादन प्रारंभ के पूरा होने की प्रतीक्षा करेगा ।
इस सुविधा के लिए GCC और VS सपोर्ट ( डायनामिक इनिशियलाइज़ेशन एंड डिस्ट्रक्शन विद कंसिमेरा , जिसे MSDN पर मैजिक स्टेटिक्स भी कहा जाता है ) इस प्रकार है:
उनकी टिप्पणियों के लिए @Mankarse और @olen_gam को धन्यवाद।
में सी ++ 03 , इस कोड को सुरक्षित थ्रेड नहीं किया गया। "सी ++ और डबल-चेक्ड लॉकिंग के पर्ल्स" नामक मेयर्स का एक लेख है, जिसमें पैटर्न के थ्रेड सुरक्षित कार्यान्वयन पर चर्चा की गई है, और निष्कर्ष यह है कि कम या ज्यादा, (C ++ 03 में) इंस्टेंटिंग विधि के आसपास पूर्ण लॉकिंग मूल रूप से सभी प्लेटफार्मों पर उचित संगामिति सुनिश्चित करने का सबसे सरल तरीका है, जबकि डबल-चेकिंग लॉकिंग पैटर्न वेरिएंट के अधिकांश रूप कुछ आर्किटेक्चर पर दौड़ की स्थिति से पीड़ित हो सकते हैं , जब तक कि निर्देशों को रणनीतिक रूप से मेमोरी बाधाओं के साथ हस्तक्षेप नहीं किया जाता है।
यह थ्रेडसेफ़ क्यों नहीं है के बारे में आपके प्रश्न का उत्तर देने के लिए, यह नहीं है क्योंकि पहली कॉल के लिए instance()कंस्ट्रक्टर को कॉल करना होगा Singleton s। थ्रेडसेफ़ होने के लिए यह एक महत्वपूर्ण अनुभाग में होना होगा, और लेकिन मानक में कोई आवश्यकता नहीं है कि एक महत्वपूर्ण अनुभाग लिया जाए (तिथि करने के लिए मानक थ्रेड पर पूरी तरह से चुप है)। संकलक अक्सर एक साधारण चेक और एक स्थिर बूलियन की वृद्धि का उपयोग करके इसे लागू करते हैं - लेकिन एक महत्वपूर्ण खंड में नहीं। निम्नलिखित छद्मकोड की तरह कुछ:
static Singleton& instance()
{
static bool initialized = false;
static char s[sizeof( Singleton)];
if (!initialized) {
initialized = true;
new( &s) Singleton(); // call placement new on s to construct it
}
return (*(reinterpret_cast<Singleton*>( &s)));
}
तो यहाँ एक सरल धागा-सुरक्षित सिंगलटन (विंडोज के लिए) है। यह विंडोज CRITICAL_SECTION ऑब्जेक्ट के लिए एक साधारण वर्ग आवरण का उपयोग करता है ताकि हम कंपाइलर को स्वचालित रूप से CRITICAL_SECTIONपहले main()कहे जाने वाले को इनिशियलाइज़ कर सकें । आदर्श रूप से एक सच्चा RAII क्रिटिकल सेक्शन क्लास का उपयोग किया जाएगा जो कि महत्वपूर्ण सेक्शन होने पर होने वाले अपवादों से निपट सकता है, लेकिन यह इस उत्तर के दायरे से परे है।
मौलिक संचालन यह है कि जब एक उदाहरण का Singletonअनुरोध किया जाता है, तो एक ताला लिया जाता है, सिंग्लटन बनाया जाता है यदि इसे होने की आवश्यकता होती है, तो लॉक जारी किया जाता है और सिंगलटन संदर्भ वापस आ जाता है।
#include <windows.h>
class CritSection : public CRITICAL_SECTION
{
public:
CritSection() {
InitializeCriticalSection( this);
}
~CritSection() {
DeleteCriticalSection( this);
}
private:
// disable copy and assignment of CritSection
CritSection( CritSection const&);
CritSection& operator=( CritSection const&);
};
class Singleton
{
public:
static Singleton& instance();
private:
// don't allow public construct/destruct
Singleton();
~Singleton();
// disable copy & assignment
Singleton( Singleton const&);
Singleton& operator=( Singleton const&);
static CritSection instance_lock;
};
CritSection Singleton::instance_lock; // definition for Singleton's lock
// it's initialized before main() is called
Singleton::Singleton()
{
}
Singleton& Singleton::instance()
{
// check to see if we need to create the Singleton
EnterCriticalSection( &instance_lock);
static Singleton s;
LeaveCriticalSection( &instance_lock);
return s;
}
आदमी - कि "बेहतर वैश्विक बनाने के लिए" बहुत बकवास है।
इस कार्यान्वयन में मुख्य कमियां हैं (यदि मैंने कुछ बगों को खिसकने नहीं दिया):
new Singleton()फेंकता है, तो लॉक जारी नहीं किया जाएगा। यह एक सरल RAII लॉक ऑब्जेक्ट का उपयोग करके तय किया जा सकता है जो मेरे पास यहां है। यह चीजों को पोर्टेबल बनाने में भी मदद कर सकता है यदि आप लॉक के लिए प्लेटफ़ॉर्म स्वतंत्र आवरण प्रदान करने के लिए बूस्ट जैसी किसी चीज़ का उपयोग करते हैं।main()जाता है - यदि आप इसे पहले कहते हैं (जैसे स्थिर वस्तु के आरंभीकरण में) चीजें काम नहीं कर सकती हैं क्योंकि CRITICAL_SECTIONहो सकता है कि इसे प्रारंभ न किया जाए।new Singleton()फेंकता है तो क्या होता है?
new Singleton()थ्रो होता है तो निश्चित रूप से लॉक की समस्या है। एक उचित RAII लॉक क्लास का उपयोग किया जाना चाहिए, lock_guardबूस्ट से कुछ । मैं चाहता था कि उदाहरण अधिक या कम आत्म-निहित हो, और यह पहले से ही थोड़ा सा राक्षस था इसलिए मैंने अपवाद सुरक्षा को छोड़ दिया (लेकिन इसे बाहर कहा गया)। शायद मुझे यह तय करना चाहिए कि कहीं यह कोड कट-एन-पेस्ट न हो जाए।
अगले मानक (खंड 6.7.4) को देखते हुए, यह पता चलता है कि स्थैतिक स्थानीय आरंभीकरण थ्रेड सुरक्षित कैसे है। इसलिए एक बार मानक का खंड व्यापक रूप से लागू हो जाने के बाद, मेयर का सिंगलटन पसंदीदा कार्यान्वयन होगा।
मैं पहले ही कई जवाबों से असहमत हूं। अधिकांश कंपाइलर पहले से ही इस तरह से स्थैतिक आरंभ को लागू करते हैं। एक उल्लेखनीय अपवाद Microsoft Visual Studio है।
क्या निम्नलिखित कार्यान्वयन [...] धागा सुरक्षित है?
अधिकांश प्लेटफार्मों पर, यह थ्रेड-सुरक्षित नहीं है। (सामान्य डिस्क्लेमर को स्पष्ट करते हुए बताएं कि C ++ मानक को थ्रेड्स के बारे में नहीं पता है, इसलिए, कानूनी रूप से, यह नहीं कहता है कि यह है या नहीं।)
यदि नहीं, तो क्यों […]
कारण यह नहीं है कि कुछ भी एक साथ एक से अधिक थ्रेड को sकंस्ट्रक्टर को निष्पादित करने से रोकता है ।
यह कैसे सुरक्षित धागा बनाने के लिए?
स्कॉट मेयर्स और आंद्रेई अलेक्जेंड्रेस्कु द्वारा "सी ++ और द पेरिल्स ऑफ डबल-चेक्ड लॉकिंग" थ्रेड-सुरक्षित सिंग्लेटन्स के विषय पर एक बहुत अच्छा ग्रंथ है।
जैसा कि MSalters ने कहा: यह आपके द्वारा उपयोग किए जाने वाले C ++ कार्यान्वयन पर निर्भर करता है। प्रलेखन की जाँच करें। अन्य प्रश्न के रूप में: "यदि नहीं, तो क्यों?" - C ++ मानक अभी तक थ्रेड्स के बारे में कुछ भी उल्लेख नहीं करता है। लेकिन आगामी C ++ संस्करण थ्रेड्स के बारे में पता है और यह स्पष्ट रूप से बताता है कि स्थैतिक स्थानीय लोगों का आरंभ थ्रेड-सुरक्षित है। यदि दो थ्रेड्स ऐसे फ़ंक्शन को कॉल करते हैं, तो एक थ्रेड एक इनिशियलाइज़ेशन करेगा जबकि दूसरा ब्लॉक करेगा और इसके खत्म होने का इंतजार करेगा।