如何高效地使用std::async对指针数组进行操作

How to use std::async efficiently to perform operations on pointer array

我是现代 C++ 库的新手,正在尝试学习如何使用 std::async 对大指针数组执行一些操作。我编写的示例代码在启动异步任务时崩溃。

示例代码:

#include <iostream>
#include <future>
#include <tuple>
#include <numeric>


#define maximum(a,b)            (((a) > (b)) ? (a) : (b))

class Foo {
    bool flag;

public:

    Foo(bool b) : flag(b) {}

    //******
    //
    //******
    std::tuple<long long, int> calc(int* a, int begIdx, int endIdx) {
        long sum = 0;
        int max = 0;

        if (!(*this).flag) {
            return std::make_tuple(sum, max);
        }

        if (endIdx - begIdx < 100)
        {
            for (int i = begIdx; i < endIdx; ++i)
            {
                sum += a[i];
                if (max < a[i])
                    max = a[i];
            }
            return std::make_tuple(sum, max);
        }

        int midIdx = endIdx / 2;
        auto handle = std::async(&Foo::calc, this, std::ref(a), midIdx, endIdx);
        auto resultTuple = calc(a, begIdx, midIdx);
        auto asyncTuple = handle.get();

        sum = std::get<0>(asyncTuple) +std::get<0>(resultTuple);
        max = maximum(std::get<1>(asyncTuple), std::get<1>(resultTuple));

        return std::make_tuple(sum, max);
    }

    //******
    //
    //******
    void call_calc(int*& a) {
        auto handle = std::async(&Foo::calc, this, std::ref(a), 0, 10000);
        auto resultTuple = handle.get();

        std::cout << "Sum = " << std::get<0>(resultTuple) << "  Maximum = " << std::get<1>(resultTuple) << std::endl;
    }
};

//******
//
//******
int main() {
    int* nums = new int[10000];
    for (int i = 0; i < 10000; ++i)
        nums[i] = rand() % 10000 + 1;

    Foo foo(true);
    foo.call_calc(nums);

    delete[] nums;
}

谁能帮我确定为什么会崩溃? 有没有更好的方法将并行性应用于大指针数组的操作?

根本问题是您的代码想要启动超过数组大小/100 个线程。这意味着超过 100 个线程。 100 个线程不会做任何好事;他们会吵架。请参阅 std::thread::hardware_concurrency,并且通常不要在生产应用程序中使用原始 asyncthread;写任务池,拼接futures之类的

那么多线程效率极低,而且可能会耗尽系统资源。

第二个问题是你计算2个值的平均值失败。

begIdxendIdx 的平均值不是 endIdx/2 而是:

int midIdx = begIdx + (endIdx-begIdx) / 2;

Live example.

您会注意到我通过添加中间输出发现了您程序的问题。特别是,我让它打印出它正在处理的范围,我注意到它在重复范围。这被称为 "printf debugging",并且非常强大,尤其是当基于步骤的调试不是这样的时候(有这么多线程,单步执行代码会让人大脑麻木)

异步调用的问题在于它们无法在某个可以同时执行无限量任务的宇宙中完成。

异步调用在具有一定数量 processors/cores 的处理器上执行,异步调用必须排队才能在它们上执行。

这里是同步问题、阻塞问题、饥饿问题……以及其他多线程问题发挥作用的地方。

您的算法很难遵循,因为它在已创建的任务中生成任务。事情正在发生,但很难跟进。

我会通过以下方式解决这个问题:

  1. 创建结果向量(将来自异步线程)
  2. 在循环中执行异步调用(将结果分配给向量)
  3. 之后循环通过 reuslts 向量收集结果