इसलिए मैंने अब कई लेखों का दावा किया है कि सी ++ डबल चेक लॉकिंग पर, आमतौर पर कई धागे को आलसी रूप से बनाए गए सिंगलटन को प्रारंभ करने की कोशिश करने से रोकने के लिए उपयोग किया जाता है। सामान्य डबल जाँच की लॉकिंग कोड इस तरह पढ़ता है:डबल चेक किए गए लॉकिंग के लिए इस फ़िक्स के साथ क्या गलत है?
class singleton {
private:
singleton(); // private constructor so users must call instance()
static boost::mutex _init_mutex;
public:
static singleton & instance()
{
static singleton* instance;
if(!instance)
{
boost::mutex::scoped_lock lock(_init_mutex);
if(!instance)
instance = new singleton;
}
return *instance;
}
};
समस्या जाहिरा तौर पर रेखा आवंटित उदाहरण है - संकलक ऑब्जेक्ट को आवंटित है और फिर इसे करने के लिए सूचक असाइन करें, या जहां यह करने के लिए सूचक स्थापित करने के लिए करने के लिए स्वतंत्र है आवंटित किया जाएगा, फिर इसे आवंटित किया जाएगा। बाद वाला मामला मुहावरे को तोड़ देता है - एक धागा स्मृति आवंटित कर सकता है और सूचक को आवंटित कर सकता है लेकिन सिंगलटन के कन्स्ट्रक्टर को नींद में आने से पहले नहीं चलाता है - फिर दूसरा धागा देखता है कि उदाहरण शून्य नहीं है और इसे वापस करने का प्रयास करें , भले ही इसे अभी तक नहीं बनाया गया है।
I saw a suggestion थ्रेड स्थानीय बूलियन का उपयोग करने के लिए और instance
की बजाय जांचें। कुछ इस तरह:
class singleton {
private:
singleton(); // private constructor so users must call instance()
static boost::mutex _init_mutex;
static boost::thread_specific_ptr<int> _sync_check;
public:
static singleton & instance()
{
static singleton* instance;
if(!_sync_check.get())
{
boost::mutex::scoped_lock lock(_init_mutex);
if(!instance)
instance = new singleton;
// Any non-null value would work, we're really just using it as a
// thread specific bool.
_sync_check = reinterpret_cast<int*>(1);
}
return *instance;
}
};
इस तरह प्रत्येक थ्रेड पता चल सके कि उदाहरण एक बार बनाया गया है समाप्त होता है, लेकिन उसके बाद बंद हो जाता है, जो कुछ प्रदर्शन हिट जरूरत पर जोर देता, लेकिन अभी भी लगभग इतनी हर कॉल पर ताला लगा के रूप में बुरा नहीं। लेकिन क्या होगा यदि हमने अभी एक स्थानीय स्थैतिक बूल का उपयोग किया ?:
class singleton {
private:
singleton(); // private constructor so users must call instance()
static boost::mutex _init_mutex;
public:
static singleton & instance()
{
static bool sync_check = false;
static singleton* instance;
if(!sync_check)
{
boost::mutex::scoped_lock lock(_init_mutex);
if(!instance)
instance = new singleton;
sync_check = true;
}
return *instance;
}
};
यह काम क्यों नहीं करेगा? यहां तक कि सिंक_चेक को एक थ्रेड द्वारा पढ़ा जाना था, जब इसे दूसरे में सौंपा जा रहा है तो कचरा मूल्य अभी भी nonzero और इस प्रकार सच होगा। This Dr. Dobb's article का दावा है कि आपको लॉक करना है क्योंकि आप कभी भी निर्देशक निर्देशों पर कंपाइलर के साथ लड़ाई नहीं जीतेंगे। जो मुझे लगता है कि यह किसी कारण से काम नहीं करना चाहिए, लेकिन मैं यह नहीं समझ सकता कि क्यों। यदि अनुक्रम बिंदुओं की आवश्यकताएं खो जाती हैं क्योंकि डॉ। डॉब के लेख मुझे विश्वास करते हैं, तो मुझे समझ में नहीं आता कि किसी भी कोड को लॉक से पहले लॉक करने के बाद पुन: व्यवस्थित नहीं किया जा सका। जो सी ++ मल्टीथ्रेडिंग टूटी अवधि बना देगा।
मुझे लगता है कि मैं संकलक को विशेष रूप से लॉक से पहले होने के लिए sync_check को पुन: व्यवस्थित करने की अनुमति दे रहा हूं क्योंकि यह एक स्थानीय चर है (और भले ही यह स्थिर है कि हम संदर्भ या सूचक नहीं लौट रहे हैं) - लेकिन फिर यह इसके बजाय इसे स्थिर सदस्य (प्रभावशाली वैश्विक) बनाकर हल किया जा सकता है।
तो क्या यह काम करेगा या नहीं? क्यूं कर?
समस्या यह है कि कन्स्ट्रक्टर को चलाने से पहले वैरिएबल को असाइन किया जा सकता है (या पूरा हो जाता है), ऑब्जेक्ट आवंटित होने से पहले नहीं। – kdgregory
धन्यवाद, सही। मैंने दौड़ की स्थिति पूरी तरह से गलत समझा था। –
हां, आप सही हैं, वर्तमान सी ++ वास्तव में "टूटी अवधि बहुसंख्यक" है। केवल मानक पर विचार करते समय। कंपाइलर विक्रेता सामान्य रूप से इसके आसपास के तरीके प्रदान करते हैं, इसलिए व्यावहारिक परिणाम उस भयानक नहीं हैं। – Suma