2

IUnknown COMインターフェイスのReleaseメソッドを実装する標準的な (推奨されるとは言いません)方法を次に示します (MSDN から直接取得)。

ULONG CMyMAPIObject::Release()
{
    // Decrement the object's internal counter.
    ULONG ulRefCount = InterlockedDecrement(m_cRef);
    if (0 == m_cRef)
    {
        delete this;
    }
    return ulRefCount;
}

アパートメント モデルがSTAでない場合、競合状態が発生する可能性があるかどうか疑問に思っていました。

  • 参照が1つ残っていると言う
  • スレッド 1 は、 Releaseを呼び出してそれを解放します
  • 実行し、直前に停止しますdelete this
  • スレッド 2 がスケジュールされ、たとえばQueryInterfaceまたはAddRefを呼び出して、オブジェクトへの新しい参照を取得します。
  • スレッド 1 は引き続き実行され、実行されますdelete this
  • スレッド 2 には無効なオブジェクトが残っています

私にとって、一貫性を確保する唯一の方法は、deletedなどのフラグを作成し、クリティカル セクション全体、つまりreturn を除くすべてのReleaseメソッドをロックし、フラグをtrueに設定することです。

AddRefメソッドとQueryInterfaceメソッドでこのフラグをチェックし、設定されている場合は、新しい参照のリクエストを拒否します。

私は何が欠けていますか?

前もって感謝します。

4

2 に答える 2

6

スレッド 2 がスケジュールされ、たとえば QueryInterface または AddRef を呼び出して、オブジェクトへの新しい参照を取得します。

これを行うことができるのは、オブジェクトによって実装されている IUnknown または他のインターフェイスの 1 つへの参照が既にある場合のみです。以前に AddRef() 呼び出しが行われたもの。したがって、参照カウントは、別のスレッドの Release 呼び出しによって 1 未満の値に減少することはありません。

コードを正しく記述してください。m_cRef ではなく、ulRefCount を 0 と比較する必要があります。

于 2013-10-02T17:47:16.730 に答える
4

コードには競合状態がありますが、あなたの例のものではありません。のこの実装にRelease()は、未定義の動作 (通常は「ダブル フリー」) を引き起こす可能性のある競合状態があります。検討:

  1. スレッド 1 とスレッド 2 にはオブジェクトへの参照があります ( m_cRef== 2)
  2. スレッド 1 が呼び出さRelease()れ、実行直後に中断されるInterlockedDecrement()( m_cRef== 2)
  3. スレッド 2 が呼び出さRelease()れ、完了するまで実行されるため、m_cRef== 0 が呼び出されますdelete this
  4. スレッド 1 は行if (0 == m_cRef)and m_cRef== 0 で再開するため、delete this再度呼び出して未定義の動作を引き起こします (通常は「ダブル フリー」エラー)。

正しい実装は次のとおりです。

ULONG CMyMAPIObject::Release()
{
    // Decrement the object's internal counter.
    ULONG ulRefCount = InterlockedDecrement(m_cRef);
    if (0 == ulRefCount) //<<<< THIS FIXES THE PROBLEM
    {
        delete this;
    }
    return ulRefCount;
}

ulRefCountif チェックは、ローカル変数に対して行う必要があります。InterlockedDecrement()デクリメントした値を返すためulRefCount、スレッド 2 の呼び出しではゼロになるだけなので、スレッド 2delete thisでのみ呼び出されます。

if (0 == m_cRef)ロックなしで共有状態にアクセスしており、安全ではありません。

この質問への回答も参照してください: COM IUnknown::Release のこの実装が機能するのはなぜですか?

于 2013-10-02T18:01:46.960 に答える