2017-06-26 29 views
0

競合状態またはメモリ破損の正確な原因を突き止めるのに問題があります。問題を解決する私の試みは、コードの後に​​表示されます。C++ std :: threadの競合状態またはメモリ破損

私は次の構造を持っている:コードを実行するには

class A 
{ 
protected: 
    // various variables 
    // 1. vector that is assigned value on B, C, D constructor and not 
    // modified while in thread 
    // 2. various ints 
    // 3. double array that is accessed by B, C, D 
    // here that are used by B, C and D 
public: 
    virtual void execute() = 0; 
}; 

class B : A 
{ 
public: 
    B(...){}; 
    bool isFinished(); 
    void execute(); //execute does a very expensive loop (genetic algorithm) 
} 

class C : A 
{ 
public: 
    C(...){}; 
    bool isFinished(); 
    void execute(); 
} 

class D : A 
{ 
public: 
    D(...){}; 
    bool isFinished(); 
    void execute(); 
} 

class Worker 
{ 
private: 
    A& m_a; 
    Container& m_parent; 
public: 
    // Worker needs a reference to parent container to control a mutex 
    // in the sync version of this code (not shown here) 
    Worker(A& aa, Container& parent) : m_a(aa), m_parent(parent) {} 
    executeAsynchronous(); 
} 

class Container 
{ 
private: 
    std::vector<Worker> wVec; 
public: 
    addWorker(Worker w); //this does wVec.push_back(w) 
    start(); 
} 

void Worker::executeAsynchronous(){ 
    while(!a.isFinished()) 
     m_a.execute(); 
} 

void Container::start(){ 
    std::thread threads[3]; 
    for (int i=0; i<wVec.size(); i++){ 
     threads[i] = std::thread(&Worker::executeAsynchronous, 
               std::ref(wVec[i])); 
    } 
    for (int i=0; i<wVec.size(); i++){ 
     threads[i].join(); 
    } 
} 

を、私はしたい:

Container container; 
B b(...); 
C c(...); 
D d(...); 
Worker worker1(b, container); 
Worker worker2(c, container); 
Worker worker3(d, container); 
container.addWorker(worker1); 
container.addWorker(worker2); 
container.addWorker(worker3); 
container.start(); 

コードは、私は次のことを持っているが、非同期​​を実行するためのスレッドを生成することになっています2つの問題:

  1. 1つのスレッドが2つまたは3つまたは4つのスレッドより速いの方が良い結果(1つのスレッドで遺伝的アルゴリズムを実行した結果、より良い最適化が得られる)は、I could be limited by memory bandwidthを読んだことがありますが、それはどこで起こっていますか?これが事実であることをどのように確認できますか?

  2. 2つ以上のスレッド:結果が非常に悪くなり、何らかの形で壊れたり途中で乱れたりします。しかし、私はそれを特定することはできません。コード内のさまざまな場所からcoutがあり、各スレッドは、継承されたクラスの​​を正確に実行します。すなわち、各スレッドは​​のB, C or Dを実行し、他のスレッドに干渉しません。私がm_parent.mutex.lock()m_parent.mutex.unlock()a.execute();の周りに置いた瞬間、マルチスレッドコードは効果的にシングルスレッドになり、結果は再び正確になります。バックContainerのベクターにWorkersを押した後、ダングリングになる可能性がありB, C and D

    1. 削除ポインタ:

    私がしようとしてきました。今私はpush_backにコピーを渡します。

  3. 代わりpush_backの使用emplace_backが、それは私がSTDを発見したので、再配分や損失リファレンスのが、違いはありません
  4. 使用std::ref()を回避するためには差
  5. 使用vector.reserve()をしなかっ::スレッドは、コピーを作成し、私は要素がwVec[i]したいです変更する前に、私はちょうどwVec[i]をスレッドに渡していました。

私は上記の1-4を行うことによって、違いはなく、コードをシングルスレッドで実行することによって、範囲外のものではないことを完全に実証します。 スレッドやコンテナ間のデータ交換もありません。std::vectorはスレッドセーフではありません。

私がこのことを理解するのに時間がかかるのであれば幸いです。

EDIT1:私は破損の問題を解決した:コンスタンティンパンの予告を1として、ここに私のRandomNumberGeneratorクラスがあり、それは私がEDIT2 RandomNumberGenerator::getDouble(a,b)

//rng.h 
class RandomNumberGenerator 
{ 
private: 
    static std::mt19937 rng; 

public: 
    static void initRNG(); 
    static int getInt(int min, int max); 
    static double getDouble(double min, double max); 
}; 

//rng.cpp 
std::mt19937 RandomNumberGenerator::rng; 

void RandomNumberGenerator::initRNG() 
{ 
    rng.seed(std::random_device()()); 
} 

int RandomNumberGenerator::getInt(int min, int max) 
{ 
    std::uniform_int_distribution<std::mt19937::result_type> udist(min, max); 
    return udist(rng); 
} 

double RandomNumberGenerator::getDouble(double min, double max) 
{ 
    std::uniform_real_distribution<> udist(min, max); 
    return udist(rng); 
} 

を使用してそれを呼び出す、静的なクラスです。これは私が逃した非スレッドセーフな関数(評価関数)への呼び出しでした。遅さについては、スレッドで実行したときにプログラムはまだ遅いです。私はvalgrindのcallgrindを実行し、gprof2dotを使用して結果をグラフ化し、M4rcの提案が表示されます。 STLコンテナ呼び出しがたくさんあるので、代わりに動的に配列を割り当てようとします。

EDIT3: RNGクラスのように見えるが、Constantin Panが指摘したような犯人だった。 gprof

Flat profile: 

Each sample counts as 0.01 seconds. 
    % cumulative self    self  total   
time seconds seconds calls s/call s/call name  
17.97  70.09 70.09 1734468  0.00  0.00 std::mersenne_twister_engine //SYNC 
18.33  64.98 64.98 1803194  0.00  0.00 std::mersenne_twister_engine //ASYNC 
    6.19  63.41  8.93 1185214  0.00  0.00 std::mersenne_twister_engine //Single thread 

EDIT4を使用して、プロファイル:のDequeコンテナはあまりにも有罪だった - M4rc

Each sample counts as 0.01 seconds. 
    % cumulative self    self  total   
time seconds seconds calls s/call s/call name  
14.15  28.60 28.60 799662660  0.00  0.00 std::_Deque_iterator 
+1

コードが機能しない理由を理解するには、実際には動作しないコードを投稿する必要があります。 [mcve]これはマルチスレッドコードで特に重要です。アプリケーションをできるだけ小さくしてエラーを残すまでアプリケーションを減らしてください。まだヘルプが必要な場合は、そのコードを投稿してください。 – xaxxon

+0

プロファイラは減速の原因を説明しているかもしれませんが、私の推測はstlアロケータがそこの原因です。私は同様の問題を抱えていましたが、かなり多くのスレッドがありました。ちょうど笑顔のために、おそらく動的にオブジェクトをプッシュバックするのではなく、逆戻りするものにポインタを動的に割り当てることができます。 – M4rc

+0

@ M4rcこれまでのところ私の知見をもとに質問(Edit2)を更新しました。私はあなたの提案を試みます。 – kamala11

答えて

0

SICE関わる遺伝的アルゴリズムが存在し、乱数生成器は、スレッドセーフであることを確認してください。私はrand()からcstdlibにこれを(過去の減速と間違った結果)自分自身に当てました。

+0

私はrngクラスを編集して追加しました。これはスレッドセーフでなければならない静的クラスです – kamala11