2011-09-11 9 views
6

अगर मैं एक IUnknown *ptr है, मैं ptr->Release() बुला जब मैं ptr पूरा कर लेने के अलावा में हर इंटरफ़ेस मैं ptr->QueryInterface() के माध्यम से प्राप्त करने पर Release(), कॉल करने के लिए की जरूरत है?क्या अज्ञात :: QueryInterface() संदर्भ गणना में वृद्धि करता है?

मुझे लगता है कि करने के लिए है कि इसका जवाब "हाँ" है, लेकिन this quote from MSDN मुझे उलझन में:

कभी कभी

आप एक वस्तु के लिए एक कमजोर संदर्भ (प्राप्त करने के लिए है कि है, तो आप एक सूचक प्राप्त करने के लिए इच्छा हो सकती है आवश्यकता हो सकती है संदर्भ गणना में वृद्धि किए बिना इसके इंटरफ़ेस में से एक), लेकिन QueryInterface पर कॉल करके Release पर कॉल करके इसे स्वीकार्य नहीं है।

मुझे समझ नहीं आता क्यों कि समस्याग्रस्त है - अगर मैं ptr->QueryInterface() फोन और फिर जिसके परिणामस्वरूप सूचक पर Release कहते हैं, नहीं वस्तु पर संदर्भ गिनती अभी भी सकारात्मक होना चाहिए? इसका परिणाम अमान्य सूचक में कैसे होता है?

उत्तर

6

प्रलेखन पर सही है। और आपको संदर्भ गणना नियमों का पालन करना होगा - जिसमें ऑब्जेक्ट बनाने के बाद QueryInterface से प्राप्त इंटरफेस पर Release को कॉल करना शामिल है।

यह साफ़ करने के लिए कि आप Release के साथ कमजोर पॉइंटर्स क्यों नहीं कर सकते हैं - QueryInterface और फिर Release पर कॉल करने में दौड़ की स्थिति मौजूद है।

  • Thread1 बनाता वस्तु - संदर्भ गिनती 1
  • Thread2 कमजोर संदर्भ के लिए QueryInterface कॉल - संदर्भ संख्या 2
  • Thread1 विज्ञप्ति आपत्ति - संदर्भ गिनती 1
  • Thread2 कॉल कमजोर संदर्भ के लिए Release - संदर्भ गिनती 0। वस्तु नष्ट हो गई है।
  • थ्रेड 2 ऑब्जेक्ट - त्रुटि का उपयोग करने का प्रयास करता है।

चेतावनी ऊपर से सुरक्षा के लिए है - शायद कुछ प्रोग्रामर लगता है कि वे "ptr->QueryInterface() फोन और फिर जिसके परिणामस्वरूप सूचक पर Release फोन" और फिर वस्तु का उपयोग कर सकते हैं ...

+0

आह ... तो यह थ्रेडिंग के कारण है, मुझे इसका एहसास नहीं हुआ। धन्यवाद! +1 – Mehrdad

+2

यहां तक ​​कि थ्रेडिंग भी नहीं, अगर आप * कमजोर * से पहले अपने * मजबूत * (इसे इसे कॉल करें) संदर्भ 'रिलीज़ करें' तो एक * कमजोर * संदर्भ अब वैध नहीं है। कॉलिंग 'रिलीज' मूल रूप से कहने का आपका तरीका है "ठीक है मैं कर चुका हूं" - एक बार जब आप इसे कॉल करते हैं, तो ऑब्जेक्ट का उपयोग करना बंद करें। यदि आपको ऑब्जेक्ट का उपयोग करना जारी रखना है ... 'रिलीज' को कॉल न करें। –

+0

दाएं; मैं केवल उस मामले के बारे में सोच रहा था जहां आप 'इंटरफेस' को 'इंटरफ़ेस' पर कॉल करते हैं * तुरंत 'आपकी' क्वेरी इंटरफेस 'कॉल के बाद, और इसलिए मैंने दौड़ की स्थिति के बारे में नहीं सोचा था। – Mehrdad

4

IUnknown :: QueryInterface विधि

एक वस्तु पर समर्थित इंटरफेस की ओर इशारा पुन: प्राप्त करता।

यह विधि IU अज्ञात :: AddRef को पॉइंटर पर लौटाती है।

सीधे IUnknown :: QueryInterface संदर्भ से

http://msdn.microsoft.com/en-us/library/ms682521%28v=vs.85%29.aspx

2

Theading नहीं है एकमात्र परिदृश्य; मैं अब तक कहूंगा कि थ्रेडिंग वास्तव में प्राथमिक परिदृश्य नहीं है: इन COM नियमों को पहले से ही विंडोज़ में प्रीपेप्टिव मल्टीथ्रेडिंग जोड़ा जाने से पहले Win16 की तारीख है।

प्रमुख मुद्दा यह है कि जहाँ तक कॉम का संबंध है, संदर्भ मायने रखता है प्रति- हैं इंटरफ़ेस, वस्तु प्रति- नहीं। एक COM कार्यान्वयन वास्तव में प्रति-ऑब्जेक्ट को कार्यान्वित करके संदर्भ गणना लागू करने के लिए स्वतंत्र है - यह शायद C++ में ऐसा करने का सबसे आसान तरीका है, खासकर जब COM COM एक एकल C++ ऑब्जेक्ट पर मानचित्र करता है - लेकिन यह कार्यान्वयन विस्तार से अधिक कुछ नहीं है, और COM क्लाइंट कोड इस मामले पर भरोसा नहीं कर सकता है।

वहां कई COM ऑब्जेक्ट्स हैं जो आवश्यकतानुसार फ्लाई पर इंटरफेस उत्पन्न कर सकती हैं, और फिर उन्हें जितनी जल्दी आवश्यकता हो उतनी नष्ट कर सकती है। उन मामलों में, यदि आप रिलीज को कॉल करने के बाद इन इंटरफेस में से एक प्राप्त करने के लिए क्यूआई को कॉल करते हैं, तो उस इंटरफ़ेस की स्मृति को हटाया जा सकता है, इसलिए इसका उपयोग करने से गलती/क्रैश/आदि हो सकती है।

आम तौर पर, आपको किसी भी कॉल को -> रिलीज() को पॉइंटर के पीछे स्मृति को संभावित रूप से हटाने के रूप में मानना ​​होगा।

+0

मेरा प्रारंभिक अनुमान यह भी था कि संदर्भ गणना प्रति-इंटरफ़ेस हैं, लेकिन मुझे नहीं लगता कि वास्तव में यह हो सकता है, क्योंकि COM नियम बताते हैं कि ['अज्ञात' पर दो बार 'QueryInterface' को कॉल करना ** ** ** को वापस देना होगा * * वही ** भौतिक सूचक] (http://msdn.microsoft.com/en-us/library/ms682521.aspx)। तो वास्तव में, ऐसा लगता है कि * हर बार एक ही वस्तु होने के लिए * है। – Mehrdad

+2

_only_ स्थान जहां COM को एक ही सूचक मूल्य की आवश्यकता होती है, जहां आप विशेष रूप से IU अज्ञात के लिए क्यूआई हैं; यह एक विशेष मामला है जिसे कैनोलिक I अज्ञात कहा जाता है। अन्य सभी इंटरफेस संभावित रूप से मांग पर उत्पन्न हो सकते हैं। (यह वास्तव में यह एक मामला है जो अन्य इंटरफेस को मांग पर रखने के लिए _allows_ करता है, और अभी भी कैनोनिकल IU अज्ञात की तुलना करके ऑब्जेक्ट पहचान की तुलना करने की कुछ क्षमता है। प्रति-इंटरफेस गिनती केवल कुछ सैद्धांतिक बात नहीं है: एटीएल के लिए इसका समर्थन है, http://msdn.microsoft.com/en-us/library/wh8b86c9(v=vs.80).aspx देखें) – BrendanMcK

0

सुझाव कमजोर संदर्भ दौड़ का समाधान नहीं करता से बचने के लिए:

(वहाँ गणना की जाती है (मजबूत) संदर्भ, और बस इतना ही इसके अलावा, ध्यान दें कि COM वास्तव में कमजोर संदर्भ के साथ शुरू करने के लिए की एक अवधारणा नहीं है।) मुद्दा।

T1 operator new, create object, references: 1 
T1  passes interface object reference to T2, thinking it can "share" ownership 
T1  suspends 
T2  resumes 
T2 QueryInterface 
T2  suspends before InterlockedIncrement, references: 1 
T1  resumes 
T1 Calls Release 
T1  suspends between InterlockedDecrement and operator delete, references: 0 
T2  resumes, InterlockedIncrement occurs, references 1 
T2  suspends 
T1  resumes, operator delete executes, references 1 !!! 
T1  suspends 
T2  resumes 
T2 Any reference to the interface is now invalid since it has been deleted with reference count 1. 

यह COM सर्वर में हल करने योग्य है। हालांकि, COM क्लाइंट को इस दौड़ की स्थिति को रोकने वाले सर्वर पर निर्भर नहीं होना चाहिए। इसलिए, COM क्लाइंट को थ्रेड के बीच इंटरफ़ेस ऑब्जेक्ट्स साझा नहीं करना चाहिए। इंटरफ़ेस ऑब्जेक्ट तक पहुंचने की अनुमति देने वाला एकमात्र धागा, एक थ्रेड है जो वर्तमान में इंटरफ़ेस ऑब्जेक्ट का "मालिक" है।

टी 1 को रिलीज नहीं कहा जाना चाहिए था। हां, यह इंटरफ़ेस ऑब्जेक्ट को T2 पर पास करने से पहले AddRef कहा जा सकता था। लेकिन वह दौड़ को हल नहीं कर सकता है, केवल इसे किसी और जगह ले जाएं। सबसे अच्छा अभ्यास हमेशा एक-इंटरफ़ेस-ऑब्जेक्ट, एक-स्वामी की अवधारणा को बनाए रखना है।

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

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