競合状態またはメモリ破損の正確な原因を突き止めるのに問題があります。問題を解決する私の試みは、コードの後に表示されます。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つのスレッドが2つまたは3つまたは4つのスレッドより速いとの方が良い結果(1つのスレッドで遺伝的アルゴリズムを実行した結果、より良い最適化が得られる)は、I could be limited by memory bandwidthを読んだことがありますが、それはどこで起こっていますか?これが事実であることをどのように確認できますか?
2つ以上のスレッド:結果が非常に悪くなり、何らかの形で壊れたり途中で乱れたりします。しかし、私はそれを特定することはできません。コード内のさまざまな場所から
cout
があり、各スレッドは、継承されたクラスのを正確に実行します。すなわち、各スレッドはのB, C or D
を実行し、他のスレッドに干渉しません。私がm_parent.mutex.lock()
とm_parent.mutex.unlock()
をa.execute();
の周りに置いた瞬間、マルチスレッドコードは効果的にシングルスレッドになり、結果は再び正確になります。バックContainer
のベクターにWorkers
を押した後、ダングリングになる可能性がありB, C and D
で- 削除ポインタ:
私がしようとしてきました。今私は
push_back
にコピーを渡します。- 代わり
push_back
の使用emplace_back
が、それは私がSTDを発見したので、再配分や損失リファレンスのが、違いはありません - 使用
std::ref()
を回避するためには差 - 使用
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
コードが機能しない理由を理解するには、実際には動作しないコードを投稿する必要があります。 [mcve]これはマルチスレッドコードで特に重要です。アプリケーションをできるだけ小さくしてエラーを残すまでアプリケーションを減らしてください。まだヘルプが必要な場合は、そのコードを投稿してください。 – xaxxon
プロファイラは減速の原因を説明しているかもしれませんが、私の推測はstlアロケータがそこの原因です。私は同様の問題を抱えていましたが、かなり多くのスレッドがありました。ちょうど笑顔のために、おそらく動的にオブジェクトをプッシュバックするのではなく、逆戻りするものにポインタを動的に割り当てることができます。 – M4rc
@ M4rcこれまでのところ私の知見をもとに質問(Edit2)を更新しました。私はあなたの提案を試みます。 – kamala11