iunknown :: queryinterface()は参照カウントを増やしますか?
-
28-10-2019 - |
質問
私が持っている場合 IUnknown *ptr
, 、電話する必要がありますか Release()
私が取得するすべてのインターフェイスで ptr->QueryInterface()
, 更に 電話する ptr->Release()
私が終わったら ptr
?
答えは「はい」だと思っていましたが、 MSDNからのこの引用 混乱した:
時折、オブジェクトへの弱い参照を取得する必要がある場合があります(つまり、参照カウントを増やすことなく、そのインターフェイスの1つへのポインターを取得することをお勧めします) しかし、呼び出すことによってこれを行うことは受け入れられません
QueryInterface
に続くRelease
.
なぜそれが問題なのかわかりません - 私が電話した場合 ptr->QueryInterface()
そして、電話してください Release
結果のポインターでは、オブジェクトの参照カウントがまだ正であるべきではありませんか?それはどのようにして無効なポインターをもたらしますか?
解決
ドキュメントは正しいです。そして、あなたは参照カウントルールに従う必要があります - それは呼び出しを含みます Release
から得られたインターフェイス上 QueryInterface
オブジェクトを作成した後に加えて。
あなたが弱いポインターをすることができない理由をクリアするために Release
- 呼び出しには人種条件が存在します QueryInterface
その後 Release
その直後。
- Thread1はオブジェクトを作成します - 参照カウント1
- thread2呼び出し
QueryInterface
弱い参照の場合 - 参照カウント2 - Thread1リリースオブジェクト - 参照カウント1
- thread2呼び出し
Release
弱い参照の場合 - 参照カウント0.オブジェクトが破壊されます。 - Thread2はオブジェクト - エラーを使用しようとします。
警告は上記を守るためにそこにあります - おそらく一部のプログラマーは彼らが「電話することができると考えています ptr->QueryInterface()
そして、電話してください Release
結果のポインター」で」を使用して、オブジェクトを使用します...
他のヒント
iunknown :: queryinterfaceメソッド
オブジェクト上のサポートされているインターフェイスへのポインターを取得します。
このメソッドは、返されるポインター上のaddRefを呼び出します。
iunknown :: queryinterfaceリファレンスから直接http://msdn.microsoft.com/en-us/library/ms682521%28v = vs.85%29.aspx
シナリオだけではありません。スレッドは実際には主要なシナリオではないと言っているところまで行きます。これらのcomルールは、最初にウィンドウに先制的なマルチスレッドが追加される前にWin16に戻ります。
重要な問題は、COMに関する限り、参照カウントがパスであるということです。インターフェース, 、あまりではありません物体. 。 comの実装は、オブジェクトごとに実装することでリファレンスカウントを実際に実装できます。これは、特にCOMオブジェクトが単一のC ++オブジェクトにマップする場合、おそらくC ++で最も簡単な方法ですが、それは実装の詳細に他なりません。また、COMクライアントコードはそうであることに頼ることはできません。
必要に応じてその場でインターフェイスを生成し、それらが不要になったらすぐにそれらを破壊する可能性のある多くのcomオブジェクトがあります。そのような場合、QIに電話してこれらのインターフェイスのいずれかを取得すると、リリースを呼び出すと、そのインターフェイスのメモリを処理できるため、使用すると障害/クラッシュなどが発生する可能性があります。
一般的に言えば、 - > release()への呼び出しは、ポインターの背後にあるメモリを潜在的に扱うと見なす必要があります。
(また、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つのスレッドです。
T1はリリースと呼ばれるべきではありません。はい、インターフェイスオブジェクトをT2に渡す前に、AddRefと呼ばれていた可能性があります。しかし、それはレースを解決しないかもしれませんし、他の場所に移動するだけです。ベストプラクティスは、常に1インターフェイスオブジェクトの1つの所有者の概念を維持することです。
COMサーバーが、2つの(またはそれ以上の)インターフェイスが共有サーバー内部状態を参照できるという概念をサポートしたい場合、COMサーバーはCreateCopyofInstanceメソッドを提供し、内部的に競合を管理することにより契約を宣伝する必要があります。もちろん、この種の「ファンアウト」を処理するサーバーの例があります。 Microsoftの永続的なストレージインターフェイスをご覧ください。そこで、インターフェイスは「ファンアウト」ではありません。各インターフェイスは、単一のユーザー(スレッド/プロセス/何でも)が所有する必要があり、「ファンアウト」はサーバーによって内部的に管理され、メソッドが提供されます。 comクライアントは、競合問題のいくつかのファセットを制御します。したがって、MicrosoftのCOMサーバーは、クライアントとの契約の一環として、レース条件に対処する必要があります。