我对现代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
,并且通常不要在生产应用程序中使用原始async
或thread
;编写任务池并将期货等拼接在一起。
如此多的线程效率极低,并且可能会耗尽系统资源。
第二个问题是你无法计算出 2 个值的平均值。
begIdx
和endIdx
的平均值不是endIdx/2
而是:
int midIdx = begIdx + (endIdx-begIdx) / 2;
活生生的例子。
您会注意到我通过添加中间输出发现了您的程序的问题。 特别是,我让它打印出它正在处理的范围,我注意到它是重复的范围。 这被称为"printf 调试",并且非常强大,尤其是在没有基于步骤的调试时(有这么多线程,单步执行代码会让人麻木(
异步调用的问题在于,它们不是在可以同时执行无限数量任务的宇宙中完成的。
异步调用在具有一定数量的处理器/内核的处理器上执行,并且异步调用必须排队才能在其上执行。
现在这里是同步问题,阻塞,饥饿问题,...和其他多线程问题开始发挥作用。
您的算法很难遵循,因为它在已经创建的任务中生成任务。有些事情正在发生,但很难遵循。
我会通过以下方式解决这个问题:
- 创建结果向量(将来自异步线程(
- 在循环中执行异步调用(将结果分配给向量(
- 然后循环遍历 reuslts 向量收集结果