使用std::async异步函数,在结果就绪时立即显示结果

5

我想要学习C++中的异步编程。这是我一直在使用的一个玩具示例:

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

#include <chrono>
#include <thread>

#include <random>

// For simplicity
using namespace std;

int called_from_async(int m, int n)
{
    this_thread::sleep_for(chrono::milliseconds(rand() % 1000));
    return m * n;
}

void test()
{
    int m = 12;
    int n = 42;

    vector<future<int>> results;

    for(int i = 0; i < 10; i++)
    {
        for(int j = 0; j < 10; j++)
        {
            results.push_back(async(launch::async, called_from_async, i, j));
        }
    }

    for(auto& f : results)
    {
        cout << f.get() << endl;
    }
}

现在,这个例子并不是很有趣,但它引发了一个对我来说有趣的问题。假设我想要按照它们的到达顺序显示结果(因为延迟是随机的,我不知道哪个会先完成),我应该如何做呢?
显然,我现在所做的是错误的,因为我按照创建它们的顺序等待所有任务——所以即使其中一个比其他任务慢,我也会等待它完成。
我想到了以下的想法:对于每个future,使用wait_for等待一小段时间,如果已准备好,就显示其值。但我感觉这样做很奇怪:
while (any_of(results.begin(), results.end(), [](const future<int>& f){
    return f.wait_for(chrono::seconds(0)) != future_status::ready;
}))
{
    cout << "Loop" << endl;
    for(auto& f : results)
    {
        auto result = f.wait_for(std::chrono::milliseconds(20));
        if (result == future_status::ready)
            cout << f.get() << endl;
    }
}

这带来了另一个问题:我们会在一些future上多次调用get,这是不合法的:
终止,抛出 'std::future_error' 实例 what(): std::future_error: 没有关联状态
所以我真的不知道该怎么办,请给出建议!
1个回答

4
使用 valid() 可以跳过已经调用了 get() 的 futures。
bool all_ready;
do {
    all_ready = true;
    for(auto& f : results) {
        if (f.valid()) {
            auto result = f.wait_for(std::chrono::milliseconds(20));
            if (result == future_status::ready) {
                cout << f.get() << endl;
            }
            else {
                all_ready = false;
            }
        }
    }
}
while (!all_ready);

2
并不是真正的解决方案。代码仍然会在每个任务上等待20毫秒,按照它们被创建的顺序。这可能会产生一种错觉,即结果“尽快”可用,并且对于简单情况可能足够。但是,如果尝试使用100个任务,先创建慢的,后创建快的,那么这种方法就不再适用了。一个真正的解决方案需要涉及到mutexcondition_variable和某种结果队列。 - j6t

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接