Implement search using futures, async, and thread in C++11

試著忘記壹切 提交于 2019-12-10 20:08:14

问题


I would like to implement branch and bound search in a multithreaded manner. In particular, I want to use async to wrap the search calls at each branch, then just wait until some thread comes up with the answer, and just exit. (Ideally, I would want to cancel the other threads, but thread cancelling is not in the standard). Here is some simplified code :

#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() is the search function. It returns true/false based on whether it found the answer or not. I return a random answer for illustration. But the crux of the problem is in the for loop that calls tasks[i].wait(). Right now, I am waiting sequentially for tasks to finish. Instead I want to do something like this :

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

What is a good way to achieve this?


回答1:


std::future provides a valid() function that lets you check if the result is available without blocking, so you can just use that, e.g. in a busy-wait loop:

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();

A proposed addition to std::future, to make tasks like this easier, is a .then(func_obj) method that asynchronously calls the func_obj when the result is available, where you could set a flag or something.

I sadly don't know of a way to possibly implement wait_for_any in any other way than above. :/

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();
  });
}

Thread destruction is usually done with cooperative cancellation.

P. S.: std::future<T>::get() will automatically wait() if the result is not available.




回答2:


Arrange for all tasks to have access to the same condition_variable, mutex, and bool. This could be done by making these globals, or member data where each task is running a member function, or you could pass them via std::ref as arguments in the task creation function.

Initialize the bool to not_found prior to starting any tasks. The main thread then launches the tasks and waits on the condition_variable. The searcher tasks then search. As they search, they occasionally inspect the bool (perhaps with an atomic load) to see if it has been set to found. If it has, the searcher thread returns immediately.

When one thread finds the results, it sets the bool to found and signals the condition_variable. This will wake the main thread and effectively cancel the rest of the searcher tasks. The main thread can then either join, detach, abandon, whatever, with all of the searcher tasks. It would be best if you could arrange for all of the searcher tasks to end prior to main exiting if you don't have main explicitly join the searcher tasks.

No polling. No waiting for dead-end searches. The only ad-hoc part is figuring out how and how often for the searcher tasks to inspect the bool. I recommend performance testing this part.




回答3:


The approach that I would take is to have the main function create a std::promise which is then passed as a reference to the various threads that are going to do the work. Each of the working threads would then use the std::promise they all share to use set_value() once they have the result.

Which ever working thread gets there first will be able to send a result and the others will get an exception thrown when they try to use set_value() and it has already been set.

In the source code skeleton that follows I am using std::async but it would be nice if we could just spin off autonomous threads without creating the additional std::promise which is not used for anything.

So the code skeleton would look something like:

#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;
}


来源:https://stackoverflow.com/questions/11910051/implement-search-using-futures-async-and-thread-in-c11

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!