c ++ std :: thread

时间:2017-06-26 22:52:54

标签: c++ multithreading stdthread

我遇到了麻烦,指出了竞争条件或内存损坏的确切来源。我在代码后面显示了解决问题的尝试。

我有以下结构:

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();

代码应该生成线程以异步方式运行execute()但是我有以下两个问题:

  1. 一个线程快于2或3或4个线程 AND 具有更好的结果(通过在1个线程中运行遗传算法得到更好的优化),我已经读过{{3}但是那里发生了什么?如何验证是否属实?

  2. 两个或多个线程:结果变得非常糟糕,不知何故,某些东西在整个过程中被破坏或损坏。但是我无法确定它。我从代码中的不同位置编辑cout,每个线程只执行一个继承的类execute(),即每个线程运行execute()的{​​{1}}并且不会#39;跳或干扰他人。我将B, C or Dm_parent.mutex.lock()放在m_parent.mutex.unlock()周围,有效地使多线程代码成为单线程,结果再次变得正确。

  3. 我试图:

    1. 删除a.execute();中可能在将B, C and D推回Workers向量后可能会悬空的指针。我现在将副本传递给Container
    2. 使用push_back代替emplace_back,但没有区别
    3. 使用push_back来避免重新分配和丢失引用但没有区别
    4. 使用vector.reserve(),因为我发现std :: thread会复制,我希望修改元素std::ref(),之前我只是将wVec[i]传递给线程。
    5. 我相信通过上面的1-4做了它们并没有任何区别,并且通过运行代码单线程并且它完美地工作,这不是一个超出范围的事情。 线程或容器之间也没有数据交换,我知道wVec[i]不是线程安全的。

      如果您花时间帮助我解决这个问题,我会感激不尽。

      EDIT1:根据Constantin Pan的通知,这是我的RandomNumberGenerator类,它是一个静态类,我用std::vector

      调用它
      RandomNumberGenerator::getDouble(a,b)

      EDIT2:我已经解决了腐败问题。这是对我错过的非线程安全函数的调用(评估函数)。至于速度慢,程序在线程中运行时仍然很慢。我已经运行了valgrind //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); } 并使用callgrind绘制了结果,看起来M4rc的建议成立了。有很多STL容器调用,我将尝试动态分配数组。

      EDIT3:看起来像Constantin Pan指出的RNG类是罪魁祸首。使用gprof2dot

      进行概述
      gprof

      EDIT4 :Deque容器也很有罪 - M4rc

      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
      

1 个答案:

答案 0 :(得分:0)

Sice涉及遗传算法,确保随机数生成器是线程安全的。我在rand() cstdlib {{1}}打了这个(减速和不正确的结果)。