2009-03-14 27 views
15

मैं एक विरासत कोड के माध्यम से जा रहा है और निम्नलिखित स्निपेट मिला था:क्या विनाशक थ्रेडसेफ होना चाहिए?

MyClass::~MyClass() 
{ 
    EnterCriticalSection(&cs); 

//Access Data Members, **NO Global** members are being accessed here 


    LeaveCriticalSection(&cs); 
} 

मैं इसे नाशक रक्षा करने के लिए किसी भी संयोग से मदद मिलेगी सोच रहा हूँ?

एक परिदृश्य पर विचार करें:

1. Thread1 - About to execute any of the member function which uses critical section 
2. Thread2- About to execute destructor. 

हैं निष्पादन के आदेश 1 = है> 2 तो यह काम हो सकता है। लेकिन अगर आदेश उलट दिया गया है तो क्या होगा?

क्या यह एक डिजाइन मुद्दा है?

उत्तर

34

विनाशक को ऑब्जेक्ट उपयोग में आने पर नहीं कहा जाना चाहिए। यदि आप ऐसी स्थिति से निपट रहे हैं, इसे एक मौलिक फिक्स की आवश्यकता है। हालांकि, विनाशक कुछ अन्य चीज को बदलना चाहेगा (जो कक्षा को नष्ट करने के लिए असंबंधित है) और इसे एक महत्वपूर्ण खंड की आवश्यकता हो सकती है (उदाहरण के लिए वैश्विक काउंटर को कम करना)।

+0

लेकिन क्या यह स्वयं धागा-सुरक्षित होना चाहिए? क्या कोई मौका है कि एक वर्ग को समानांतर में "दो बार नष्ट कर दिया जाएगा"? –

+0

@ टॉमसज़ेटो: नहीं। यदि एक वर्ग (एक गैर-तुच्छ विनाशक के साथ) "दो बार नष्ट हो जाता है" इससे कोई फर्क नहीं पड़ता कि यह थ्रेडसेफ तरीके से होता है या नहीं - यह किसी भी मामले में यूबी है। – MikeMB

+1

उत्तर की उम्र के बावजूद, मैं असहमत हूं। एक सिंक्रनाइज़ेशन लागू किया जाना चाहिए यदि सदस्य चर को थ्रेड की तुलना में किसी अन्य थ्रेड द्वारा संशोधित किया गया है जहां dtor कहा जाएगा।हालांकि, इसे "महत्वपूर्ण खंड" होने की आवश्यकता नहीं है। क्या आवश्यक है, कि दो धागे के बीच संबंध "सिंक्रनाइज़ेशन-विद" है। इसके लिए धागे में उपयुक्त मेमोरी बाधाओं की आवश्यकता हो सकती है - जब तक कि धागे समान न हों। – CouchDeveloper

3

मुझे लगता है कि आपके पास एक और मौलिक समस्या है। किसी ऑब्जेक्ट को एक थ्रेड पर नष्ट करना कानूनी नहीं होना चाहिए, जबकि दूसरा थ्रेड अभी भी सदस्य कार्यों को कॉल कर रहा है। यह स्वयं में गलत है।

भले ही आप महत्वपूर्ण खंडों के साथ अपने विनाशक की सफलतापूर्वक रक्षा करते हैं, तब भी होता है जब अन्य थ्रेड फ़ंक्शन के शेष को निष्पादित करना प्रारंभ करता है? यह एक हटाए गए ऑब्जेक्ट पर ऐसा करेगा जो (इसके आवंटन स्थान के आधार पर) कचरा स्मृति या साधारण अमान्य वस्तु होगी।

आपको यह सुनिश्चित करने के लिए अपना कोड बदलने की जरूरत है कि ऑब्जेक्ट अभी भी उपयोग में नहीं है।

3

"थ्रेड सुरक्षित" परिभाषित करें। ये आधुनिक कंप्यूटिंग में संभवतः दो सबसे बुरी तरह से समझने वाले शब्द हैं।

लेकिन अगर विनाशक को दो अलग-अलग धागे से दो बार दर्ज किया जा रहा है (सिमक्रोनिसेशन ऑब्जेक्ट्स के उपयोग के रूप में) आपका कोड गहरी डू-डू में है। ऑब्जेक्ट जो आप जिस ऑब्जेक्ट के बारे में पूछ रहे हैं उसे हटा रहे हैं, इसे प्रबंधित करना चाहिए - यह उस स्तर पर (शायद) है कि सिंक्रनाइज़ेशन होना चाहिए।

0

कोई फर्क नहीं पड़ता। यदि, जैसा कि आप कहते हैं, कॉल का क्रम उलट दिया गया है तो आप एक अवरुद्ध वस्तु पर सदस्य फ़ंक्शन को कॉल कर रहे हैं और यह असफल होने जा रहा है। सिंक्रनाइज़ेशन उस तार्किक त्रुटि को ठीक नहीं कर सकता है (स्टार्टर्स के लिए, सदस्य फ़ंक्शन कॉल एक लॉक ऑब्जेक्ट प्राप्त करने का प्रयास करेगा जो नष्ट हो गया है)।

4

आप वैश्विक चर प्रवेश कर रहे हैं आप धागा सुरक्षा की जरूरत हो सकती है, हाँ

जैसे। मेरा "विंडो" वर्ग खुद को कन्स्ट्रक्टर में "ज्ञातविंडोज़" सूची में जोड़ता है और खुद को विनाशक में हटा देता है। "ज्ञात विन्डोज़" को थ्रेडसेफ होना चाहिए ताकि वे दोनों एक म्यूटेक्स को लॉक कर दें।

दूसरी ओर, यदि आपका विनाशक केवल वस्तु के सदस्यों को नष्ट कर देता है, तो आपके पास एक डिज़ाइन समस्या है।

+0

यह सही जवाब है। –

0

मैं नील बटरवर्थ से टिप्पणी करता हूं। बिल्कुल, माइक्लास को हटाने और एक्सेस करने के लिए जिम्मेदार संस्थाओं को इस पर एक जांच करनी चाहिए।

यह सिंक्रनाइज़ेशन वास्तव में उस समय से शुरू होगा जब MyClass टाइप किया गया है।

2

मैंने एसीई धागे के साथ एक मामला देखा है, जहां थ्रेड ACE_Task_Base ऑब्जेक्ट पर चल रहा है और ऑब्जेक्ट किसी अन्य थ्रेड से नष्ट हो गया है। विनाशक एक ताला प्राप्त करता है और एक शर्त पर इंतजार करने से पहले, निहित धागे को सूचित करता है। धागा कि ACE_Task_Base संकेत पर चल रहा है निकास पर हालत का संकेत और नाशक पूर्ण और पहले धागा बाहर निकलने की सुविधा देता है:

class PeriodicThread : public ACE_Task_Base 
{ 
public: 
    PeriodicThread() : exit_(false), mutex_() 
    { 
    } 
    ~PeriodicThread() 
    { 
     mutex_.acquire(); 
     exit_ = true; 
     mutex_.release(); 
     wait(); // wait for running thread to exit 
    } 
    int svc() 
    { 
     mutex_.acquire(); 
     while (!exit_) { 
     mutex_.release(); 
     // perform periodic operation 
     mutex_.acquire(); 
     } 
     mutex_.release(); 
    } 
private: 
    bool exit_; 
    ACE_Thread_Mutex mutex_; 
}; 

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

0

आपकी टिप्पणियां कहती हैं "कोई वैश्विक सदस्यों को यहां एक्सेस किया जा रहा है" तो मुझे नहीं लगता। केवल एक थ्रेड जिसने ऑब्जेक्ट बनाया है उसे नष्ट करना चाहिए, इसलिए आप किस अन्य धागे से इसकी रक्षा करेंगे?

मुझे व्यवस्थित सृजन और विनाश पसंद है, जहां केवल एक ही वस्तु का एक और उप-वस्तु का मालिक होता है, और उस उप-वस्तु के संदर्भ में कोई अन्य वस्तु पेड़ में एक वंशज है। यदि उनमें से कोई भी उप-वस्तुएं विभिन्न धागे का प्रतिनिधित्व करती हैं, तो वे पेड़ को नष्ट करने से पहले पूरा करना सुनिश्चित कर लेंगे।

उदाहरण:

  • मुख्य() वस्तु बनाने के एक
    • वस्तु एक शामिल वस्तु बी
      • वस्तु बी
        • वस्तु सी एक धागा है कि पहुँचता बनाता वस्तु सी होता है ऑब्जेक्ट्स ए और बी
        • सी नाशक रन वस्तु, के लिए
      • वस्तु बी नाशक
    • वस्तु एक नाशक चलाता
  • मुख्य (चलाता) रिटर्न

विनाशकर्ता खत्म करने के लिए अपने थ्रेड के लिए प्रतीक्षा ऑब्जेक्ट ए और बी को थ्रेड के बारे में सोचने की आवश्यकता नहीं है, और ऑब्जेक्ट के विनाशक को केवल कुछ संचार तंत्र को लागू करने की आवश्यकता है (उदाहरण के लिए, किसी ईवेंट पर प्रतीक्षा करना) धागे के साथ खुद को बनाने के लिए चुना।

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

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