0

最近、私は 1 つのレガシー プロジェクトに取り組み始め、セグメンテーション違反 (二重削除) を修正しようとしました。それらの多くは、boost::shared_ptrデストラクタまたはoperator=(shared_ptr を含むオブジェクト) で発生しています。コードには、コピー、reset()-ing、割り当てなどを含む、shared_ptr-s の大規模な使用が含まれています。boost ドキュメントによると、有効な使用法がありません。多くのスレッドで同じ shared_ptr を破棄/コピー/リセットすることは安全ではありません。

毎回ロックするのは不可能に思えるので、boost::shared_ptr のドロップイン代替品を探しています。boost::shared_ptr問題は、すべてを置き換えるstd::shared_ptrstd::tr1::shared_ptr、この問題を解決するかということです。tr1 はより安全なバージョンのようですが、私にはわかりません。2 番目の質問 - c++0x バージョンは tr1 より優れていますか? (gcc 4.4.6 があり、アップグレードできないことに注意してください)

gcc docsによると、c++11 std::shared_ptr はそれを修正する必要がありますが、gcc4.4 バージョンについてはわかりません...

UPD:実験を行ったところ、3つの実装すべてがこのコード(gcc 4.4)でsegfaultを行うことがわかりました..カスタムクラスを作成するか、他の回避策を作成する必要があるようです...

#include <iostream>
#include <boost/thread.hpp>
#include <boost/shared_ptr.hpp>

typedef boost::shared_ptr<int> ptrtype;

ptrtype p(new int);

void test() {
        for(long i=0; i<1000000; ++i) {
                ptrtype p1 = p;
                p = ptrtype();
                p.reset( new int );
        }
}

int main() {
        boost::thread_group tg;
        for(int i=0; i<100; ++i) tg.add_thread( new boost::thread(test) );
        tg.join_all();
        std::cout << "Normal exit\n";
        return 0;
}
4

3 に答える 3

1

ステップ 1: このようなクラスを作成し、の使用法をboost::shared_ptr<T>それに置き換えます。

template<typename T>
struct trivial_ptr {
  T* t;
  template<typename U>
  void reset( U* p ) {t=p;}
  void reset( T* p = NULL ) { t=p; }
  template<typename U>
  trivial_ptr<T>& operator=(trivial_shared_ptr<U>const& o) {
    t = o.t;
    return *t;
  }
  explicit trivial_ptr( T* p ):t(p) {}
  ...
};

このクラスは実行するためのものではなく、正しいインターフェースでコンパイルするためのものです。boost::shared_ptrコンパイルが完了すると、インターフェイスのどの部分を使用しているかがわかります。(カスタムのデリータを使用していますか?など -- 問題はより難しく、またはより簡単になる可能性があり、上記はそれをテストするのに役立ちます)

shared_ptr<T>ここまで来れば、同じ変数に同時にアクセスする複数のスレッドを処理するを作成するのがいかに難しいかがわかります。

さて、これは非常に厄介です。別のスレッドがそこから読み取っている間にあるスレッドがreset指定された場合、読み取り元のスレッドがアクセスできるようになるshared_ptrまでに、読み取られたポインターは完全に無効になる可能性があります。実際には、ミューテックス内の基になるポインターへのすべてのアクセスを保護する必要がありますが、これはまったく非現実的です。

一方、複数のリーダーがあり、リーダーとライターがない場合は、より良い状態になります。理論的には、参照カウント コードに適切なロックを使用することで問題を解決できます。

ただし、実際に説明しているのは、同じ変数への読み取りと書き込みの両方に複数のスレッドが関係しているようです。そして、それは根本的に壊れており、shared_ptr変数のスレッドセーフだけでは修正されません。

于 2013-02-27T20:53:02.823 に答える
1

あなたが抱えていると思われる問題は、2 つの別々のスレッド (別名データ競合) で変数の同じインスタンスを変更しようとすることです。shared_ptr は、これに対して int よりも保護されていません。gcc docs への参照は同じことを言っています(「組み込み型と同じレベルのスレッドセーフ」)。2 つの異なるスレッドで shared_ptr の同じインスタンスを変更しようとすると、データ競合を防ぐために何らかの同期が必要になります。同じオブジェクトを指している shared_ptr の 2 つの異なるインスタンスを変更しようとしても問題ありません (データ競合がないか、内部でデータ競合を防ぐために必要なものを shared_ptr が実装する必要があります)。それらが指すオブジェクトを変更しようとすることも、データ競合です。

于 2013-02-27T21:47:58.840 に答える
0

std::shared_ptr コンパイラ/アーキテクチャ/実装がアトミックintをサポート/使用している場合は、アトミックintを使用できますしかし、私はそれには賭けません。そうすることで、コードの移植性が低下します。ただし、一時的な回避策として機能する場合があります(たとえば、実行中のプログラムを使用して、コードの機能を理解する)。

独自のshared_ptrラッパーを作成することもできますが、コードのデッドロックを監査する必要があります。

于 2013-02-27T19:37:13.290 に答える