在C ++ 11中使用期货,异步和线程实现搜索

时间:2012-08-10 22:27:12

标签: c++ multithreading asynchronous c++11 future

我想以多线程方式实现分支和边界搜索。特别是,我想使用async来包装每个分支的搜索调用,然后等待一些线程提出答案,然后退出。 (理想情况下,我想取消其他线程,但线程取消不在标准中)。这是一些简化的代码:

#include <iostream>
#include <random>
#include <future>
#include <thread>

using namespace std;

mt19937 rng;
uniform_int_distribution<unsigned> random_binary(0, 1);

bool search() {
  return static_cast<bool>(random_binary(rng));
}

#define N 10000

int main()
{
  rng.seed(42);

  std::vector<future<bool>> tasks;

  for (unsigned i=0; i<N; ++i)
    tasks.push_back(async(launch::async, search));

  // Don't want to wait sequentially here.
  for (unsigned i=0; i<N; ++i) {
    tasks[i].wait();
    if (tasks[i].get()) {
      cout << "i = " << i << "\n";
      break;
    }
  }
  return 0;
}

search()是搜索功能。它根据是否找到答案返回true / false。我回答一个随机的答案来说明。但问题的关键在于调用tasks[i].wait()的for循环。现在,我正在等待任务完成。相反,我想做这样的事情:

auto x = wait_for_any(tasks.begin(), tasks.end());
x.get();
// cancel other threads.
// Profit?

实现这一目标的好方法是什么?

3 个答案:

答案 0 :(得分:8)

std::future提供valid()功能,可让您检查结果是否可用而不会阻止,因此您可以使用该功能,例如在忙等待循环中:

std::future<bool>* res_future = 0;
for(size_t i = 0; ; i==tasks.size()?i=0:++i){
  // could add a timeout period to not completely busy-wait the CPU
  if(tasks[i].wait_for(std::chrono::seconds(0)) == std::future_status::ready){
    res = &tasks[i];
    break;
  }
}

bool res = res_future->get();

std::future的建议添加,使这样的任务更容易,是.then(func_obj)方法,当结果可用时异步调用func_obj,您可以在其中设置标记或东西。

我遗憾地不知道如何以除上述之外的任何其他方式实现wait_for_any。 :/

template<class FwdIt>
std::future<bool> wait_for_any(FwdIt first, FwdIt last)
{
  return std::async([=]{
    for(FwdIt cur(first); ; cur==last?cur=first:++cur){
    // could add a timeout period to not completely busy-wait the CPU
    if(cur->wait_for(std::chrono::seconds(0)) == std::future_status::ready)
      return cur->get();
  });
}

线程破坏通常通过合作取消来完成。

P上。 S:std::future<T>::get()如果结果不可用,将自动wait()

答案 1 :(得分:6)

安排所有任务可以访问相同的condition_variablemutexbool。这可以通过创建这些全局变量或每个任务运行成员函数的成员数据来完成,或者您可以通过任务创建函数中的std::ref作为参数传递它们。

在开始执行任务之前,将bool初始化为not_found。然后主线程启动任务并等待condition_variable。搜索者的任务然后搜索。当他们搜索时,他们偶尔会检查bool(可能带有原子载荷)以查看它是否已设置为found。如果有,则搜索者线程立即返回。

当一个线程找到结果时,它会将bool设置为found并发出condition_variable信号。这将唤醒主线程并有效地取消其余的搜索器任务。然后,主线程可以与所有搜索器任务一起加入,分离,放弃等等。如果您没有主要明确加入搜索者任务,那么最好安排所有搜索者任务在主要退出之前结束。

没有民意调查。无需等待死胡同搜索。唯一的特殊部分是确定搜索者检查bool的任务的方式和频率。我建议对这部分进行性能测试。

答案 2 :(得分:0)

我将采用的方法是让main函数创建一个std::promise,然后将其作为对将要执行工作的各个线程的引用传递。然后,每个工作线程将使用他们共享的std::promise一旦他们获得结果就使用set_value()

哪个工作线程首先到达那里将能够发送结果,而其他人在尝试使用set_value()并且已经设置时会抛出异常。

在接下来的源代码框架中,我正在使用std::async,但如果我们可以在不创建任何未用于任何内容的附加std::promise的情况下分离自主线程,那就太好了。

因此代码框架看起来像:

#include <iostream>
#include <thread>  // std::thread is defined here
#include <future>  // std::future and std::promise defined here

bool search(int args, std::promise<int> &p) {

    // do whatever needs to be done

    try {
        p.set_value(args);
    }
    catch (std::future_error & e) {
        // std::future_errc::promise_already_satisfied
    }
    return true;
}
int main(int argc, char * argv[])
{
    std::promise<int> myProm;    
    auto fut = myProm.get_future();

    for (int i = 1; i < 4; i++) {
        std::async(std::launch::async, search, i, std::ref(myProm));
    }

    auto retVal = fut.get();
    return 0;
}