2016-01-01 17 views
5

यहाँ से: (!) Logic error in my defined Mutex class and the way I use it in producer consumer program - pthreadsम्यूटेक्स कक्षा में संदर्भ क्यों गुजर रहा है एक अच्छा डिजाइन नहीं है?

जिस तरह से आप स्पष्ट रूप से परेशानी के लिए पूछ रहा है के संदर्भ अपने म्युटेक्स वर्ग के लिए चारों ओर से पारित, यह कैप्सूलीकरण के किसी भी प्रकार खारिज कर देता है।

यह समस्या क्यों है? क्या मुझे मूल्य से गुजरना चाहिए और फिर कॉपी कन्स्ट्रक्टर लिखा जाना चाहिए?

इस मामले में encapsulation की कमी में कमी क्या हो सकती है? मुझे कैसे encapsulate करना चाहिए?

इसके अलावा, Why is passing references to the Mutex class not a good design?

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

कृपया उदाहरण के साथ सरल भाषा में समझाएं - संदर्भों को एक बुरा विचार क्यों गुजर रहा है?

+1

ठीक है, आमतौर पर आप म्यूटेक्स का उपयोग बेहद सावधानीपूर्वक तरीके से करते हैं। जब आप एक म्यूटेक्स को लॉक करते हैं, तो आप लॉक को बहुत छोटी और ध्यान से चुनी गई अवधि के लिए चाहते हैं ताकि आप डेडलॉक न करें। लॉक के संदर्भ को पास करना एक बुरा विचार है - आप लॉक का उपयोग नहीं करते हैं, आप केवल अधिग्रहण करते हैं और फिर आप इसे वापस देते हैं। इसे चारों ओर स्थानांतरित करना (महत्वपूर्ण) संसाधन के उपयोग को ट्रैक करना मुश्किल बनाता है। लॉक के बजाए एक म्यूटेक्स वैरिएबल का संदर्भ पास करना शायद इतना बुरा नहीं है, लेकिन फिर भी यह जानना मुश्किल हो जाता है कि प्रोग्राम के कौन से हिस्से डेडलॉक हो सकते हैं, इसलिए इससे बचने के लिए कुछ है। आमतौर पर आपको इसे करने की ज़रूरत नहीं होती है। –

+0

यदि आप विशेष रूप से अपने mutex wrapper वर्ग में "pthread_mutex &" गुजर रहे हैं, तो मेरा मतलब है कि यह बदसूरत है ... एक के लिए, यदि आप सावधान नहीं हैं, तो आप एक खतरनाक संदर्भ प्राप्त कर सकते हैं, और दो के लिए, आप फिर बंधे हैं pthread कार्यान्वयन के लिए। संभावित रूप से आपकी कक्षा का बिंदु कार्यान्वयन विवरण को कवर करना है, लेकिन यहां आप उन्हें तेजी से लीक कर रहे हैं! –

+0

@ChrisBeck आपके समझाए जाने के प्रयास के लिए धन्यवाद। मैं आपको "उत्तर" में समस्या और उसके समाधान को संबोधित करने का अनुरोध करता हूं। –

उत्तर

3

मैं इसे अलग प्रश्नों से अलग कर दूंगा: 1. किसी ऑब्जेक्ट को संदर्भ के अनुसार कब पास करना उचित है? 2. म्यूटेक्स साझा करना कब उचित है?

  1. जिस तरह से आप किसी ऑब्जेक्ट को तर्क के रूप में पास करते हैं, यह दर्शाता है कि आप कॉलर और कैली के बीच ऑब्जेक्ट के जीवनकाल को साझा करने की अपेक्षा कैसे करते हैं। यदि आप संदर्भ से गुजरते हैं, तो आपको यह भी मानना ​​चाहिए कि कैली केवल कॉल की अवधि के लिए ऑब्जेक्ट का उपयोग करेगा या यदि संदर्भ कैली द्वारा संग्रहीत किया जाता है, तो कैली का जीवनकाल संदर्भ से छोटा होता है। यदि गतिशील रूप से आवंटित वस्तुओं से निपटना है, तो आपको शायद स्मार्ट पॉइंटर्स का उपयोग करना चाहिए, जो (अन्य चीजों के साथ) आपको अपने इरादों को अधिक स्पष्ट रूप से संवाद करने की अनुमति देता है (इस विषय के Herb Sutter's treatment देखें)।

  2. एक म्यूटेक्स साझा करना टालना चाहिए। यह सच है कि संदर्भ या किसी अन्य माध्यम से गुजर रहा है या नहीं। एक म्यूटेक्स साझा करके, एक वस्तु बाहरी रूप से बाहरी इकाई द्वारा आंतरिक रूप से प्रभावित होने की अनुमति दे रही है। यह बुनियादी encapsulation का उल्लंघन करता है और पर्याप्त कारण है। (Encapsulation के गुणों के लिए ऑब्जेक्ट उन्मुख प्रोग्रामिंग पर कोई भी पाठ देखें)। म्यूटक्स को साझा करने में एक वास्तविक परिणाम डेडलॉक की संभावना है।

    इस सरल उदाहरण लें:

    • एक म्युटेक्स
    • बी के साथ
    • क के शेयर म्युटेक्स
    • बी ए
    • एक के समारोह पर समारोह कॉल करने से पहले ताला प्राप्त ताला प्राप्त करने का प्रयास का मालिक
    • डेडलॉक ..

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

3

मुझे लगता है कि यह खराब अमूर्तता के साथ ही खराब encapsulation है। एक mutex आमतौर पर कॉपी कन्स्ट्रक्टर के साथ डिफॉल्ट बनाया गया है, जिसमें कई म्यूटेक्स ऑब्जेक्ट्स हैं जो एक ही लॉजिकल ऑब्जेक्ट को संदर्भित करते हैं, त्रुटि प्रवण होता है, यानी यह डेडलॉक्स और अन्य रेस स्थितियों का कारण बन सकता है क्योंकि प्रोग्रामर या पाठक यह मान सकते हैं कि वे अलग-अलग इकाइयां हैं।

इसके अलावा यह निर्दिष्ट करके कि आप किस आंतरिक म्यूटेक्स का उपयोग कर रहे हैं, आप अपने धागे के कार्यान्वयन विवरण को उजागर करेंगे जिससे म्यूटेक्स वर्ग का अमूर्त टूट जाएगा। यदि आप pthread_mutex_t का उपयोग कर रहे हैं तो आप सबसे अधिक संभावना है कि कर्नेल थ्रेड (pthreads) का उपयोग करें।

Encapsulation भी टूटा हुआ है क्योंकि आपकी म्यूटेक्स एक एकल encapsulated इकाई नहीं है बल्कि कई (संभवतः लटकते) संदर्भों में बिखरी हुई है।

आप इसे क्या करना होगा आप किसी कक्षा के एक pthread_mutex_t संपुटित चाहते हैं बल्कि यह स्थानीय स्तर पर की घोषणा की और की तुलना में के रूप में तो

class Mutex { 
public: 
    void lock(); 
    void unlock(); 

    // Default and move constructors are good! 
    // You can store a mutex in STL containers with these 
    Mutex(); 
    Mutex(Mutex&&); 
    ~Mutex(); 

    // These can lead to deadlocks! 
    Mutex(const Mutex&) = delete; 
    Mutex& operator= (const Mutex&) = delete; 
    Mutex& operator= (Mutex&&) = delete; 

private: 
    pthread_mutex_t internal_mutex; 
}; 

Mutex वस्तुओं एक साझा गुंजाइश कार्यान्वयन फाइलों में घोषित में साझा करने के लिए कर रहे हैं एक संदर्भ के रूप में कार्यों में चारों ओर पारित किया। आप आदर्श रूप से थ्रेड कन्स्ट्रक्टर को केवल तर्कों में ही पारित करेंगे जो आपको चाहिए। प्रश्न में कार्य (इस मामले में थ्रेड निष्पादन) के रूप में एक ही "स्तर" पर एक दायरे में घोषित वस्तुओं के संदर्भ में ऑब्जेक्ट्स के संदर्भों को पास करना आम तौर पर कोड में त्रुटियों का कारण बनता है। क्या होता है यदि म्यूटेक्स घोषित किया गया गुंजाइश अब मौजूद नहीं है? क्या mutex के विनाशक म्यूटेक्स के आंतरिक कार्यान्वयन को अमान्य कर देगा? क्या होता है यदि म्यूटेक्स चारों ओर पारित होने के माध्यम से पूरे मॉड्यूल के लिए अपना रास्ता बनाता है और वह मॉड्यूल अपने स्वयं के धागे शुरू करता है और सोचता है कि म्यूटेक्स कभी भी अवरुद्ध नहीं होगा, इससे बुरा डेडलॉक्स हो सकता है।

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

यदि लेखक का इरादा वैश्विक दायरे से बचने के लिए था तो इसे एक स्थिर वस्तु के रूप में कार्यान्वयन फ़ाइल में एक बार घोषित करना पर्याप्त अमूर्त होना चाहिए।

+0

बहुत धन्यवाद। मैंने आपके जवाब को समझने में समय लगाया। जो मैंने समझा है वह यह है कि यदि हम संदर्भ द्वारा पारित होने के बजाय चालक कन्स्ट्रक्टर का उपयोग करते हैं, तो हमें बाहरी स्थानीय वस्तु को हटाने के बारे में चिंता करने की आवश्यकता नहीं होगी। क्या ये सही है? क्या इसके अलावा कुछ और है जिसे आप अपने उत्तर के माध्यम से व्यक्त करना चाहते थे? –

+0

हाँ मैं अपने उत्तर के माध्यम से अन्य चीजों को व्यक्त करना चाहता था। सबसे पहले चालक कन्स्ट्रक्टर म्यूटेक्स के साथ काम नहीं कर रहे हैं क्योंकि आप स्वयं उन्हें चारों ओर पास करना चाहते हैं, लेकिन इस मामले में जहां आपने म्यूटेक्स का वेक्टर कहा है और आप नए म्यूटेक्स जोड़ने के लिए वेक्टर का आकार बदलते हैं, म्यूटेक्स को एक सरणी से स्थानांतरित करने की आवश्यकता होती है दूसरे को। दूसरा मुख्य बिंदु मैं व्यक्त करना चाहता था। ज्यादातर मामलों में एक म्यूटेक्स वस्तु वैश्विक होना चाहिए। तीसरा मुख्य बिंदु जो मैं व्यक्त करना चाहता था वह था कि संदर्भ के चारों ओर एक म्यूटेक्स पास करना दौड़ की स्थिति – Curious

+0

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

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