Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Displaying results as soon as they are ready with std::async

Tags:

I'm trying to discover asynchronous programming in C++. Here's a toy example I've been using:

#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;
    }
}

Now, the example is not really interesting, but it raises a question that is, to me, interesting. Let's say I want to display results as they "arrive" (I don't know what will be ready first, since the delay is random), how should I do it?

What I'm doing here is obviously wrong, since I wait for all the tasks in the order in which I created them - so I'll wait for the first to finish even if it's longer than the others.

I thought about the following idea: for each future, using wait_for on a small time and if it's ready, display the value. But I feel weird doing that:

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;
    }
}

This brings another issue: we'd call get several times on some futures, which is illegal:

terminate called after throwing an instance of 'std::future_error'
what(): std::future_error: No associated state

So I don't really know what to do here, please suggest!

like image 965
Thomas Kowalski Avatar asked Jun 20 '19 10:06

Thomas Kowalski


1 Answers

Use valid() to skip the futures for which you have already called get().

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);
like image 86
VLL Avatar answered Nov 15 '22 07:11

VLL