7

私は現在、モノスレッドプログラムをマルチスレッドに渡そうとしています。このソフトウェアは「refCounted」オブジェクトを大量に使用するため、マルチスレッドでいくつかの問題が発生します。私の問題を解決するデザインパターンや何かを探しています。

主な問題は、スレッド間のオブジェクトの削除です。通常、削除は参照カウントを減らすだけであり、refcount がゼロに等しい場合、オブジェクトは削除されます。これはモノスレッド プログラムでうまく機能し、ビッグ オブジェクトのコピーでパフォーマンスを大幅に向上させることができます。

ただし、マルチスレッドでは、オブジェクトがミューテックスによって保護されているため、2 つのスレッドが同じオブジェクトを同時に削除したい場合があります。1 つのスレッドだけがオブジェクトを削除し、もう 1 つのスレッドをブロックします。しかし、ミューテックスを解放すると、他のスレッドは無効な (解放されたオブジェクト) で実行を継続し、メモリの破損につながります。

このクラスRefCountedObjectの例を次に示します

class RefCountedObject
{
public:
RefCountedObject()
:   _refCount( new U32(1) )
{}

RefCountedObject( const RefCountedObject& obj )
:   _refCount( obj._refCount )
{
    ACE_Guard< ACE_Mutex > guard( _refCountMutex );
    ++(*_refCount);
}

~RefCountedObject()
{
    Destroy();
}

RefCountedObject& operator=( const RefCountedObject& obj )
{
    if( this != &obj )
    {
        Destroy();
        ACE_Guard< ACE_Mutex > guard( _refCountMutex );
        _refCount = obj._refCount;
        ++(*_refCount);
    }

    return *this;
}

private:
    void Destroy()
    {
        ACE_Guard< ACE_Mutex > guard( _refCountMutex );  // thread2 are waiting here
        --(*_refCount);         // This cause a free memory write by the thread2
        if( 0 == *_refCount )
            delete _refCount;
    }

private:
    mutable U32* _refCount;
    mutable ACE_Mutex _refCountMutex; // BAD: this mutex only protect the refCount pointer, not the refCount itself
};

2 つのスレッドが同じ RefCountedObject を削除したいとします。どちらも ~RefCountedObject にあり、Destroy() を呼び出します。最初のスレッドはミューテックスをロックしており、もう 1 つのスレッドは待機しています。最初のスレッドによってオブジェクトが削除された後、2 番目のスレッドはその実行を継続し、空きメモリへの書き込みを引き起こします。

誰もが同様の問題を経験し、解決策を見つけましたか?


助けてくれてありがとう、私は私の間違いに気づきました: ミューテックスは refCount 自体ではなく、refCount ポインターのみを保護しています! ミューテックスで保護された RefCount クラスを作成しました。ミューテックスは、すべての refCounted オブジェクト間で共有されるようになりました。

今はすべて正常に動作します。

4

7 に答える 7

4

カウントがオブジェクトの一部である場合、あるスレッドが参照カウントを増やそうとしているときに別のスレッドが最後の参照を削除しようとしている場合、固有の問題があります。オブジェクトへのグローバルにアクセス可能なポインターごとに、ref カウントに追加の値が必要です。そのため、ポインターがある場合はいつでも安全に ref カウントを増やすことができます。

1 つのオプションはboost::shared_ptr (ドキュメントを参照)を使用することです。共有オブジェクトへのグローバル ポインターにアクセスするときに適切な保護を確保するために、無料の関数atomic_loadatomic_storeatomic_exchangeおよびatomic_compare_exchange(これらはドキュメントには明らかに存在しない) を使用できます。スレッドがshared_ptr特定のオブジェクトへの参照を取得したら、通常の非アトミック関数を使用してそれにアクセスできます。

別のオプションは、 Tomic_ptr_plus プロジェクトから Joe Seigh のアトミック ref-counted ポインターを使用することです。

于 2008-10-03T10:24:12.023 に答える
3

確かに、各スレッドは参照カウントを正しく管理する必要があります...つまり、ThreadAとThreadBの両方がObj1で動作している場合、ThreadAとThreadBの両方がオブジェクトへの参照を所有し、両方が完了したらreleaseを呼び出す必要があります。物体。

シングルスレッドアプリケーションでは、参照カウントオブジェクトが作成されるポイントがある可能性があります。その後、オブジェクトで作業を行い、最終的にリリースを呼び出します。マルチスレッドプログラムでは、オブジェクトを作成してからスレッドに渡します(ただし、これを行います)。オブジェクトをスレッドに渡す前に、オブジェクトでAddRef()を呼び出して、スレッドに独自の参照カウントを与える必要があります。オブジェクトを割り当てたスレッドは、オブジェクトで行われたように、releaseを呼び出すことができます。オブジェクトを操作しているスレッドは、完了時にreleaseを呼び出し、最後の参照が解放されると、オブジェクトがクリーンアップされます。

スレッド自体で実行されているコードがオブジェクトでAddRef()を呼び出さないようにする必要があることに注意してください。そうすると、ディスパッチしたスレッドの前に、オブジェクトでリリースを呼び出すスレッドを作成する間に競合状態が発生します。 AddRef()を実行して呼び出します。

于 2008-10-03T09:54:12.943 に答える
1

これは答えではありませんが、ちょっとしたアドバイスです。このような状況では、修正を開始する前に、これらの問題を確実に再現できることを確認してください。単体テストをしばらくループで実行するだけの簡単な場合もあります。競合状態を強制するために、プログラムに巧妙な Sleep を入れると役立つ場合があります。

参照カウントの問題は長引く傾向があるため、テスト ハーネスへの投資は長期的に見れば報われます。

于 2008-10-03T15:39:11.443 に答える
1

あなたの問題について少し考えてみてください...あなたが言っているのは、1つのオブジェクトがあり(refcountが1の場合)、2つのスレッドが両方ともdelete()を呼び出しているということです。これがあなたの問題が本当にあるところだと思います。

この問題を回避するもう 1 つの方法は、スレッド間で安全に再利用できるスレッド化されたオブジェクトが必要な場合、内部メモリを解放する前に refcount が 1 より大きいことを確認することです。現在、それを解放してから、refcount が 0 かどうかを確認します。

于 2008-10-03T10:25:29.760 に答える
0

スレッド間で共有しているオブジェクトはすべてミューテックスで保護する必要があり、同じことがrefcountハンドルにも当てはまります。つまり、2つのスレッドからオブジェクトへの最後の1つのハンドルを削除することはありません。1つのオブジェクトを指している2つの異なるハンドルを同時に削除している可能性があります。

Windowsでは、InterlockedDecrementを使用できます。これにより、2つのデクリメントのうち1つが0を返すことが保証されます。そのスレッドのみがrefcountedオブジェクトを削除します。

他のスレッドは、2つのハンドルのいずれかをコピーすることもできませんでした。一般的なMTルールでは、あるスレッドが別のスレッドでまだ使用されているオブジェクトを削除することはできません。これは、refcountハンドルにも適用されます。

于 2008-10-03T09:57:19.380 に答える
0

1つの解決策は、参照カウンターをアトミック値にすることです。これにより、destroyを同時に呼び出すたびに、実際に発生する削除は1つだけで安全に続行でき、もう1つはアトミック参照カウントを減らすだけです。

インテルスレッドビルディングブロックライブラリー(TBB)は、アトミック値を提供します。

また、ACE_Atomic_OpテンプレートのACEライブラリも同様です。

Boostライブラリは、これをすでに実装している参照カウントスマートポインタライブラリを提供します。

http://www.dre.vanderbilt.edu/Doxygen/Current/html/ace/a00029.html http://www.boost.org/doc/libs/release/libs/smart_ptr/shared_ptr.htm

于 2008-10-03T10:02:58.247 に答える
0

この行に沿った何かがあなたの問題を解決すると信じています:

private:
    void Destroy()
    {
ACE_Guard< ACE_Mutex > guard( _refCountMutex ); // thread2 are waiting here if (_refCount != 0) { --(*_refCount); // This cause a free memory write by the thread2 if( 0 == *_refCount ) { delete _refCount; _refcount = 0; } } } private: mutable U32* _refCount; mutable ACE_Mutex _refCountMutex;

于 2008-10-03T10:11:42.143 に答える