非阻塞pthread停止 - 或者为什么std :: atomic_flag减慢了我的代码

时间:2017-01-26 20:18:35

标签: c++ performance pthreads atomic

我实现了一个小的多线程应用程序,它执行以下操作:

MainThread

  

主线程使用setitimer启动计时器并启动最多8个   线程。来自主线程的计时器用于从>重复读取。一个文件(每0.25秒)。当计时器被调用20次(约5秒后)时,我   想要停止线程并完成计算量   每个帖子。

MainThread.h

class MainThread {
  private:
    int counter;
    ThreadManager tm;
    bool registerTimer(double seconds);
    void startTimerWithInterval(double interval);
    void read() {
      /**
       *  If counter >= 20, call stopWorker on all threads
       */
      tm.stopWorkers();
    }
  public:
    MainThread():counter(0){}
}

WorkerThreads

  

在无限循环中执行一些昂贵的计算。   经过一定量的计算后,线程必须存储   它执行的计算次数。这个值(计算量)   必须非常准确,所以我认为我必须停止线程(相当)   立刻。

ThreadClass.h

class WorkerThread { 
  private:
    /**
     * ...
     */
    std::atomic_flag keep_Running = ATOMIC_FLAG_INIT;

    static void* run(void* args) {
      ((WorkerThread*)args)->process();
      pthread_exit(nullptr);
      return nullptr;
    }

  public:
    /**
     * ...
     */
    bool startWorker() {
      keep_Running.test_and_set();
      bool result = (pthread_create(&thread, pthread_attr, run, this) == 0);
      if(!result) {
        keep_Running.clear();
      }
      return result;
    }
    void stopWorker() {
      keep_Running.clear();
    }
    bool keepRunning() {
      return keep_Running.test_and_set();
    }
    virtual void process() = 0;
};

ComputationThread.h

class ComputationThread : public WorkerThread {
  public:
    virtual void process() override {
      /**
       *  Perform computations with ~400MB data
       *  check every 16B, whether keepRunning still true
       */
      bool keep_running = true;
      while(keep_running) {
        /**
         * Process 4B
         */
        keep_running = keepRunning();
      }
    }
};

如果我使用某种标志,为了跟踪线程的运行状态,我必须让这个标志线程安全,不是吗?我尝试了std::atomic_flag,因为它应该是无锁的并且具有原子操作,但这会导致显着下降性能。我的问题是,std::atomic_flag导致性能下降还是仅仅因为我经常执行检查方式?有谁知道更好的方法?

在你提问之前,我必须使用pthread代替std::thread将线程分配给线程创建中的指定核心(使用pthread_attrib_t)。

2 个答案:

答案 0 :(得分:1)

不要使用std::atomic_flag

它意味着低级atomic原语,因此界面非常有限 它的主要限制是,您只能通过在名为atomic的单test_and_set()次呼叫中将其设置为true来测试其值。 这是一种读 - 修改 - 写操作(RMW),可在所有内核之间执行昂贵的同步。 由于您在每次循环迭代时都调用它,因此它会显着减慢。

使用常规atomic<bool>并在完成后进行设置。 这样,在循环内部,您只需要读取它,这是一个atomic加载,并转换为常规mov操作。 设置特定的内存顺序对性能没有影响(至少在X86上)。

答案 1 :(得分:0)

std::atomic_flag::test_and_set()包含默认参数std::memory_order order = memory_order_seq_cst

  

标记memory_order_seq_cst的原子操作不仅以与释放/获取排序相同的方式命令内存(发生的所有事情 - 在一个线程中的存储变为在执行加载的线程中的可见副作用之前),而且还建立所有标记的原子操作的单个总修改顺序。

     

...

     

总顺序排序需要在所有多核系统上使用完整的内存屏障CPU指令。 这可能会成为性能瓶颈,因为它会强制受影响的内存访问传播到每个核心。

memory_order的这个标志将导致每个线程按顺序执行test_and_set的内存操作,顺序加载并保存到内存,随着每个线程的运行速度会变慢花时间等待其他线程执行内存操作。