8

を持っている場合、終了時に呼び出すことに加えて、を介して取得するすべてのインターフェイスをIUnknown *ptr呼び出す必要がありますか?Release()ptr->QueryInterface()ptr->Release()ptr

以前は答えは「はい」だと思っていましたが、MSDNからのこの引用は私を混乱させました。

場合によっては、オブジェクトへの弱参照を取得する必要があります(つまり、参照カウントをインクリメントせずにそのインターフェイスの1つへのポインターを取得したい場合があります)が、その後に.を呼び出してこれを行うことはできませんQueryInterfaceRelease

なぜそれが問題になるのかわかりません-ptr->QueryInterface()呼び出しReleaseてから結果のポインタを呼び出す場合、オブジェクトの参照カウントはまだ正の値であるべきではありませんか?それはどのようにして無効なポインタになりますか?

4

4 に答える 4

8

ドキュメントは正しいです。また、参照カウントのルールに従う必要があります。これには、オブジェクトの作成後だけでなく、Releaseから取得したインターフェイスの呼び出しも含まれます。QueryInterface

弱いポインタを使用できない理由を明確にするために、呼び出しとその直後にRelease競合状態が存在します。QueryInterfaceRelease

  • Thread1はオブジェクトを作成します-参照カウント1
  • Thread2はQueryInterface弱参照を要求します-参照カウント2
  • Thread1がオブジェクトを解放します-参照カウント1
  • Thread2はRelease弱参照を要求します-参照カウント0。オブジェクトは破棄されます。
  • Thread2はオブジェクトを使用しようとします-エラー。

警告は上記を防ぐためにあります-おそらく、一部のプログラマーは、「結果のポインターptr->QueryInterface()を呼び出してから呼び出す」ことができ、オブジェクトを使用できると考えています...Release

于 2011-09-11T04:28:27.970 に答える
4

IUnknown::QueryInterfaceメソッド

オブジェクトでサポートされているインターフェイスへのポインタを取得します。

このメソッドは、返すポインターに対してIUnknown::AddRefを呼び出します。

http://msdn.microsoft.com/en-us/library/ms682521%28v=vs.85%29.aspxのIUnknown::QueryInterfaceリファレンスから直接

于 2011-09-11T04:27:53.327 に答える
2

シナリオは、Theadingだけではありません。スレッド化は実際には主要なシナリオではないと言っても過言ではありません。これらのCOMルールは、プリエンプティブマルチスレッド化が最初にWindowsに追加される前のWin16にまでさかのぼります。

重要な問題は、COMに関する限り、参照カウントはオブジェクトごとではなく、インターフェイスごとであるということです。COM実装は、オブジェクトごとに実装することで、実際に参照カウントを自由に実装できます。これは、特にCOMオブジェクトが単一のC ++オブジェクトにマップされる場合に、C ++で実行する最も簡単な方法ですが、実装の詳細にすぎません。そして、COMクライアントコードはそれが事実であると信頼することはできません。

必要に応じてその場でインターフェイスを生成し、不要になったらすぐに破棄するCOMオブジェクトがたくさんあります。このような場合、QIを呼び出してこれらのインターフェイスの1つを取得すると、Releaseを呼び出すと、そのインターフェイスのメモリが割り当て解除される可能性があるため、それを使用すると、障害やクラッシュなどが発生する可能性があります。

一般的に言って、-> Release()の呼び出しは、ポインタの背後にあるメモリの割り当てを解除する可能性があると見なす必要があります。

(また、COMにはそもそも弱い参照の概念がないことに注意してください。カウントされた(強い)参照があり、それだけです。)

于 2011-09-11T09:19:36.217 に答える
0

弱参照を回避するという提案は、レースの問題を解決しません。

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クライアントはスレッド間でインターフェイスオブジェクトを共有してはなりません(MUSTNOT)。インターフェイスオブジェクトへのアクセスを許可する必要がある唯一のスレッドは、現在インターフェイスオブジェクトを「所有」している1つのスレッドです。

T1はリリースを呼び出すべきではありませんでした。はい、インターフェイスオブジェクトをT2に渡す前にAddRefを呼び出すことができます。しかし、それはレースを解決しないかもしれません、それをどこかに移動するだけです。ベストプラクティスは、常に1つのインターフェイスオブジェクト、1つの所有者の概念を維持することです。

COMサーバーが2つ(またはそれ以上)のインターフェースが共有サーバー内部状態を参照できるという概念をサポートしたい場合、COMサーバーはCreateCopyOfInstanceメソッドを提供してコントラクトをアドバタイズし、内部で競合を管理する必要があります。もちろん、この種の「ファンアウト」を処理するサーバーの例もあります。Microsoftの永続ストレージインターフェイスを見てください。そこでは、インターフェースは「ファンアウト」しません。各インターフェースは単一のユーザー(スレッド/プロセス/その他)が所有する必要があり、「ファンアウト」はサーバーによって内部的に管理され、メソッドが提供されます。競合の問題のいくつかの側面を制御するCOMクライアント。したがって、MicrosoftのCOMサーバーは、クライアントとの契約の一部として競合状態に対処する必要があります。

于 2013-03-14T05:33:43.850 に答える