繁体   English   中英

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

[英]Non-blocking pthread stop - or why does std::atomic_flag slow down my code

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

主线程

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

主线程.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){}
}

工作线程

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

线程类.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;
};

计算线程.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 )。

不要使用std::atomic_flag

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

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

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

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

...

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

memory_order这个标志将导致每个线程按顺序执行test_and_set内存操作,顺序加载和保存到内存中,这会变慢,因为每个线程将花时间等待其他线程执行它们的内存操作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM