1

我对现代 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;
}

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

4

2 回答 2

2

根本问题是您的代码想要启动超过数组大小/100 个线程。这意味着超过 100 个线程。100 个线程不会有任何好处;他们会痛打。请参阅std::thread::hardware_concurrency,一般不要在原始应用程序asyncthread生产应用程序中使用;编写任务池并将期货等拼接在一起。

这么多线程既效率极低,又可能耗尽系统资源。

第二个问题是您未能计算 2 个值的平均值。

begIdx和的平均值endIdx不是endIdx/2而是:

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

活生生的例子

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

于 2018-05-09T12:31:26.307 回答
1

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

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

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

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

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

  1. 创建结果向量(将来自异步线程)
  2. 在循环中执行异步调用(将结果分配给向量)
  3. 然后循环遍历 reuslts 向量收集结果
于 2018-05-09T12:41:27.200 回答