如何使用线程将值异步映射到函数?

时间:2013-10-01 20:34:15

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

我正在努力学习如何在C ++ 11中执行“令人尴尬”的并行任务。我遇到的一个常见模式是在一系列值上计算函数的结果,类似于调用python的multiprocessing.Pool.map。我写了一个最小的例子来说明我知道怎么做,即调用一个进程并等待结果。如何异步“映射”此调用并等待所有值完成?理想情况下,我希望结果的矢量长度和顺序与原始相同。

#include <iostream>
#include <thread>
#include <future>
#include <vector>

using namespace std;

double square_add(double x, double y) { return x*x+y; }

int main() {
  vector<double> A = {1,2,3,4,5};

  // Single evaluation
  auto single_result = std::async(square_add,A[2],3);
  cout << "Evaluating a single index " << single_result.get() << endl;

  // Blocking map
  for(auto &x:A) {
    auto blocking_result = std::async(square_add,x,3);
    cout << "Evaluating a single index " << blocking_result.get() << endl;
  }

  // Non-blocking map?

  return 0;
}

注意:要使用gcc编译此代码,我需要-pthreads标记。

2 个答案:

答案 0 :(得分:4)

std :: async返回一个未来,因此您可以将它们存储在一个向量中以供以后使用:

std::vector<std::future<double>> future_doubles;
future_doubles.reserve(A.size());
for (auto& x : A) {
    // Might block, but also might not.
    future_doubles.push_back(std::async(square_add, x, 3));
}

// Now block on all of them one at a time.
for (auto& f_d : future_doubles) {
    std::cout << f_d.get() << std::endl;
}

现在上面的代码可能会也可能不会异步运行。由实现/系统决定是否值得以异步方式执行任务。如果要强制它在单独的线程中运行,可以将可选的launch_policy传递给std :: async,将调用更改为

future_doubles.push_back(std::async(std::launch::async, square_add, x, 3));

有关std::async及各种政策的详情,请参阅here

答案 1 :(得分:1)

has already been shown的规范/简单方式bstamour。但是,如果迭代速度很关键和/或结果类型必须为std::vector<double>,那么还有另一种选择:

std::vector<double> results(A.size());
std::vector<std::future<void>> futures;
    futures.reserve(A.size());                  // second allocation

auto store_result = [](double& result, double x, double y)
                    { result = square_add(x, y); };
// non-capturing lambda might give better performance, test it

// launch the async operations
for(int i = 0; i != A.size(); ++i)
    futures.emplace_back(std::async(store_result, std::ref(results[i]), A[i], 3));

// wait for all results to become ready
for(auto& e : futures)
    e.get();

// results are in the `results` vector
for(auto const& e : results)
    std::cout << e << std::endl;

完整示例:

#include <iostream>
#include <thread>
#include <future>
#include <vector>

using namespace std;

double square_add(double x, double y) { return x*x+y; }

int main() {
  vector<double> A = {1,2,3,4,5};

  // Single evaluation
  std::cout << "single evaluation" << std::endl;
  auto single_result = std::async(square_add,A[2],3);
  cout << "Evaluating a single index " << single_result.get() << endl;

  // Blocking map
  std::cout << "\n\n";
  std::cout << "blocking map" << std::endl;
  for(auto &x:A) {
    auto blocking_result = std::async(square_add,x,3);
    cout << "Evaluating a single index " << blocking_result.get() << endl;
  }

  // Non-blocking map?

    // bstamour's solution:
    std::cout << "\n\n";
    std::cout << "bstamour's solution" << std::endl;
    {
        std::vector<std::future<double>> future_doubles;
        future_doubles.reserve(A.size());
        for (auto& x : A) {
            // Might block, but also might not.
            future_doubles.push_back(std::async(square_add, x, 3));
        }

        // Now block on all of them one at a time.
        for (auto& f_d : future_doubles) {
            std::cout << f_d.get() << std::endl;
        }
    }

    // DyP's solution
    std::cout << "\n\n";
    std::cout << "DyP's solution" << std::endl;
    {
        std::vector<double> results(A.size());
        std::vector<std::future<void>> futures;
        futures.reserve(A.size());                  // second allocation
        auto store_result = [](double& result, double x, double y) { result = square_add(x, y); };
        for(int i = 0; i != A.size(); ++i)
            futures.emplace_back( std::async(store_result, std::ref(results[i]), A[i], 3) );

        for(auto& e : futures)
            e.get();

        for(auto const& e : results)
            std::cout << e << std::endl;
    }

  return 0;
}