繁体   English   中英

使用std :: atomic比较std :: condition_variable wrt在C ++中暂停和恢复std :: thread的方法

[英]Approach of using an std::atomic compared to std::condition_variable wrt pausing & resuming an std::thread in C++

这是一个单独的问题,但与我在此的上一个问题有关

我在我的C++代码中使用std::thread来不断轮询某些数据并将其添加到缓冲区。 我使用C++ lambda来启动这样的线程:

StartMyThread() {

    thread_running = true;
    the_thread = std::thread { [this] {
        while(thread_running) {
          GetData();
        }
    }};
}

thread_running是在类头中声明的atomic<bool> 这是我的GetData函数:

GetData() {
    //Some heavy logic
}

接下来我还有一个StopMyThread函数,我将thread_running设置为false,以便它退出lambda block中的while循环。

StopMyThread() {
  thread_running = false;
  the_thread.join();
}

据我了解,我可以暂停和使用恢复该线程std::condition_variable如指出, 这里在我前面的问题。

但是,如果我只是使用std::atomic<bool> thread_running来执行或不执行GetData()的逻辑,那么是否存在缺点?

GetData() {
    if (thread_running == false)
      return;
    //Some heavy logic
}

与使用 这里 描述的std::condition_variable的方法相比,这会消耗更多的CPU周期 吗?

当您想要有条件地停止另一个线程时,条件变量很有用。 因此,您可能有一个始终运行的“工作线程”线程,当它注意到它无法运行时,它会等待。

原子解决方案要求您的UI交互与工作线程同步,或者非常复杂的逻辑以异步方式执行。

作为一般规则,您的UI响应线程永远不应该阻止工作线程的非就绪状态。

struct worker_thread {
  worker_thread( std::function<void()> t, bool play = true ):
    task(std::move(t)),
    execute(play)
  {
    thread = std::async( std::launch::async, [this]{
      work();
    });
  }
  // move is not safe.  If you need this movable,
  // use unique_ptr<worker_thread>.
  worker_thread(worker_thread&& )=delete;
  ~worker_thread() {
    if (!exit) finalize();
    wait();
  }
  void finalize() {
    auto l = lock();
    exit = true;
    cv.notify_one();
  }
  void pause() {
    auto l = lock();
    execute = false;
  }
  void play() {
    auto l = lock();
    execute = true;
    cv.notify_one();
  }
  void wait() {
    Assert(exit);
    if (thread)
      thread.get();
  }
private:
  void work() {
    while(true) {
      bool done = false;
      {
        auto l = lock();
        cv.wait( l, [&]{
          return exit || execute;
        });
        done = exit; // have lock here
      }
      if (done) break;
      task();
    }
  }
  std::unique_lock<std::mutex> lock() {
     return std::unique_lock<std::mutex>(m);
  }
  std::mutex m;
  std::condition_variable cv;
  bool exit = false;
  bool execute = true;
  std::function<void()> task;
  std::future<void> thread;
};

或者某些。

这拥有一个主题。 只要处于play()模式,线程就会重复运行任务。 如果您在下一次task()完成时pause() ,则工作线程将停止。 如果在task()调用完成之前play() ,则不会注意到pause()

唯一的等待是破坏worker_thread ,它会自动通知它应该退出的工作线程并等待它完成。

您也可以手动.wait().finalize() .finalize()是异步的,但是如果你的应用程序正在关闭,你可以提前调用它,并让工作线程有更多的时间来清理,而主线程可以清理其他地方。

.finalize()无法逆转。

代码未经测试。

除非我遗漏了什么,否则你已经在原来的问题中回答了这个问题:每次需要时你都会创建并销毁工作线程。 这可能是您实际申请中的问题,也可能不是。

有两个不同的问题正在解决,这可能取决于你实际在做什么。 一个问题是“我希望我的线程运行直到我告诉它停止。” 另一个似乎是“我有一个生产者/消费者对,并希望能够在数据准备好时通知消费者”。 thread_runningjoin方法适用于第一个。 第二个你可能想要使用互斥和条件,因为你所做的不仅仅是使用状态来触发工作。 假设你有一个vector<Work> 你使用互斥锁保护它,所以条件变为[&work] (){ return !work.empty(); } [&work] (){ return !work.empty(); }或类似的东西。 当等待返回时,你持有互斥锁,这样你就可以把事情搞砸了。 完成后,您将返回等待,释放互斥锁,以便生产者可以向队列添加内容。

您可能希望结合使用这些技术。 有一个“完成处理”原子,你的所有线程都会定期检查以确定何时退出,以便你可以加入它们。 使用条件来涵盖线程之间的数据传递情况。

暂无
暂无

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

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