如何搜索下一个可用的线程来进行计算

时间:2017-02-16 22:01:52

标签: multithreading c++14

我在C ++中进行多线程处理。这可能是非常标准的东西,但我似乎无法在任何地方找到它或知道在线搜索它的任何关键术语。

我想多次进行某种计算但有多个线程。对于每次计算迭代,我想找到完成其先前计算的下一个可用线程以进行下一次迭代。我不想按顺序遍历线程,因为要调用的下一个线程可能还没有完成它的工作。

E.g。 假设我有一个int向量,我想用5个线程总计总数。我有一个待更新的总和存储在某个地方,以及我目前所处的元素的数量。每个线程查看计数以查看下一个位置,然后获取该向量值并将其添加到目前为止的总和中。然后它返回查找计数以执行下一次迭代。因此,对于每次迭代,计数增量然后查找下一个可用线程(可能已经等待计数;或者可能它们都忙着仍在工作)来执行下一次迭代。我们不增加线程数,但我希望能够以某种方式搜索所有5个线程,完成第一个完成下一次计算的线程。

我将如何编写此代码。我所知道的每一种方式都涉及在线程中进行循环,这样我就无法检查下一个可能出现故障的可用线程。

2 个答案:

答案 0 :(得分:0)

在全局变量上使用semafor(或互斥,总是将这两者混淆)告诉你下一步是什么。只要您访问变量使得线程访问清除,semafor就会锁定其他线程。

因此,假设您有一个X元素数组。并且一个名为nextfree的全局女孩被初始化为0,然后一个psudo代码看起来像这样:

while (1)
{
    <lock semafor INT>
    if (nextfree>=X)
    {
        <release semnafor INT>
        <exit and terminate thread>
    }
    <Get the data based on "nextfree">
    nextfree++;
    <release semafor INT>

    <do your stuff withe the chunk you got>
}

这里的要点是每个线程都是独立的,并且在semafor锁中具有对数据结构的exlusive访问权限,因此无论其他人做什么,都可以访问下一个可用的线程。 (其他线程必须排队,如果它们完成而另一个线程正在处理获取下一个数据块。当你释放只有一个站在队列中时将获得访问。其余线程将不得不等待。)

有些东西可以用来制作。如果您设法退出错误的位置(释放它)或创建死锁,Semafor可能会锁定您的系统。

答案 1 :(得分:-1)

这是一个线程池:

template<class T>
struct threaded_queue {
  using lock = std::unique_lock<std::mutex>;
  void push_back( T t ) {
    {
      lock l(m);
      data.push_back(std::move(t));
    }
    cv.notify_one();
  }
  boost::optional<T> pop_front() {
    lock l(m);
    cv.wait(l, [this]{ return abort || !data.empty(); } );
    if (abort) return {};
    auto r = std::move(data.back());
    data.pop_back();
    return std::move(r);
  }
  void terminate() {
    {
      lock l(m);
      abort = true;
      data.clear();
    }
    cv.notify_all();
  }
  ~threaded_queue()
  {
    terminate();
  }
private:
  std::mutex m;
  std::deque<T> data;
  std::condition_variable cv;
  bool abort = false;
};
struct thread_pool {
  thread_pool( std::size_t n = 1 ) { start_thread(n); }
  thread_pool( thread_pool&& ) = delete;
  thread_pool& operator=( thread_pool&& ) = delete;
  ~thread_pool() = default; // or `{ terminate(); }` if you want to abandon some tasks
  template<class F, class R=std::result_of_t<F&()>>
  std::future<R> queue_task( F task ) {
    std::packaged_task<R()> p(std::move(task));
    auto r = p.get_future();
    tasks.push_back( std::move(p) );
    return r;
  }
  template<class F, class R=std::result_of_t<F&()>>
  std::future<R> run_task( F task ) {
    if (threads_active() >= total_threads()) {
      start_thread();
    }
    return queue_task( std::move(task) );
  }
  void terminate() {
    tasks.terminate();
  }
  std::size_t threads_active() const {
    return active;
  }
  std::size_t total_threads() const {
    return threads.size();
  }
  void clear_threads() {
    terminate();
    threads.clear();
  }
  void start_thread( std::size_t n = 1 ) {
    while(n-->0) {
      threads.push_back(
        std::async( std::launch::async,
          [this]{
            while(auto task = tasks.pop_front()) {
              ++active;
              try{
                (*task)();
              } catch(...) {
                --active;
                throw;
              }
              --active;
            }
          }
        )
      );
    }
  }
private:
  std::vector<std::future<void>> threads;
  threaded_queue<std::packaged_task<void()>> tasks;
  std::atomic<std::size_t> active;
};

您可以在构造时或通过start_thread给出多少个线程。

然后你queue_task。这将返回一个std::future,告诉您任务何时完成。

当线程完成任务时,他们会转到threaded_queue并寻找更多信息。

threaded_queue被销毁时,它会中止其中的所有数据。

thread_pool被销毁时,它会中止所有未来的任务,然后等待所有未完成的任务完成。

Live example