启动多个线程,仅等待一个线程完成即可获得结果

时间:2018-12-11 14:55:44

标签: c++ multithreading c++11 boost

假设我有一个函数double someRandomFunction(int n),该函数需要一个整数并返回双精度值,但是从某种意义上说它是随机的,因为它会尝试使用随机的东西来提出解决方案,因此即使您使用相同的参数运行该函数,有时它可能需要10秒钟才能完成,而其他40秒钟才能完成。

double someRandomFunction(int n)函数本身是黑盒函数的包装。所以someRandomFunction需要一段时间才能完成,但是我在黑盒的主循环中没有控制权,因此,由于繁重的计算是在黑色中进行,因此我无法真正检查线程中的标志变量框功能。

我想启动10个调用该函数的线程,并且我对第一个首先完成的线程的结果感兴趣。我不在乎是哪一个,这些线程只需要1个结果。

我找到了以下代码:

  std::vector<boost::future<double>> futures;
  for (...) {
    auto fut = boost::async([i]() { return someRandomFunction(2) });
    futures.push_back(std::move(fut));
  }

  for (...) {
    auto res = boost::wait_for_any(futures.begin(), futures.end());
    std::this_thread::yield();
    std::cout << res->get() << std::endl;
  }

哪个是我所寻找的最接近的,但我仍然看不到如何使我的程序终止一个线程,直到一个线程返回了解决方案。

我想等待一个线程结束,然后继续执行那个线程的结果以继续执行程序(即,我不想在获得单个结果后终止程序,但是我会喜欢将其用于剩余的程序执行。)

同样,我想启动10个调用someRandomFunction的线程,然后等待一个线程首先完成,获得该线程的结果并停止所有其他线程,即使它们没有完成工作

1 个答案:

答案 0 :(得分:2)

如果提供给黑匣子的数据结构具有一些明显的开始和结束值,则使它尽早完成的一种方法可能是在计算时更改结束值。如果您误解了黑框必须如何处理数据,这当然可能会引起各种麻烦,但是如果您有把握的话,它就可以工作。

main产生100个外部线程,每个产生一个调用黑盒的内部线程。内部线程接收黑盒结果,并通知所有等待线程完成。外层线程等待 any 内层线程完成,然后修改其自己的黑盒数据以诱使它完成。

没有轮询(除了虚假的唤醒循环),也没有分离的线程。

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <chrono>

// a work package for one black-box
struct data_for_back_box {
    int start_here;
    int end_here;
};

double blackbox(data_for_back_box* data) {
    // time consuming work here:
    for(auto v=data->start_here; v<data->end_here; ++v) {
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
    // just a debug
    if(data->end_here==0) std::cout << "I was tricked into exiting early\n";
    return data->end_here;
}

// synchronizing stuff and result
std::condition_variable cv;
std::mutex mtx;
bool done=false;
double result;

// a wrapper around the real blackbox
void inner(data_for_back_box* data) {
    double r = blackbox(data);
    if(done) return; // someone has already finished, skip this result

    // notify everyone that we're done
    std::unique_lock<std::mutex> lock(mtx);
    result = r;
    done=true;
    cv.notify_all();
}

// context setup and wait for any inner wrapper
// to signal "done"
void outer(int n) {
    data_for_back_box data{0, 100+n*n};
    std::thread work(inner, &data);
    {
        std::unique_lock<std::mutex> lock(mtx);
        while( !done ) cv.wait(lock);
    }
    // corrupt data for blackbox:
    data.end_here = 0;
    // wait for this threads blackbox to finish
    work.join();
}

int main() {
    std::vector<std::thread> ths;

    // spawn 100 worker threads
    for(int i=0; i<100; ++i) {
        ths.emplace_back(outer, i);
    }

    double saved_result;
    {
        std::unique_lock<std::mutex> lock(mtx);
        while( !done ) cv.wait(lock);
        saved_result = result;
    } // release lock

    // join all threads
    std::cout << "got result, joining:\n";
    for(auto& th : ths) {
        th.join();
    }

    std::cout << "result: " << saved_result << "\n";
}