David
David

Reputation: 1017

Async with reference lambda crashing

I'm curious as to why this would crash in around 1 runs in 4.

I have a simple class that returns a string with a thread id. This class is called from within another class asynchronously in a lambda and returns the result. The code is as simple as I can make it - I'm still learning:

class String_Returner
{
public:
    string run() const
    {
        return "I returned from thread "+to_string(this_thread::get_id());
    }
};

class Class_Holder
{
public:
    Class_Holder() : number_of_runs(10)
    {
        srs.resize(number_of_runs);
    }

    void concurrent_srs()
    {
        for (int i = 0; i < number_of_runs; i++)
        {
            results.push_back(async([&]{return srs[i].run();}));
        }

        for (int i = 0; i < results.size(); i++)
        {
            cout << i << ") " << results[i].get() << endl;
        }

        results.clear();
    }
private:
    vector<future<string>> results;
    vector<String_Returner> srs;
    const int number_of_runs;
};

void class_b_test()
{
    Class_Holder ch;
    ch.concurrent_srs();
}

I realize using async with a reference is dangerous, but I figured it'd be safe if nothing is being written. I think my error probably comes from the lambda itself. This is mainly a programme to test the functionality of async and lambdas.

So my main questions: 1) Why is it crashing? 2) What better ways are there to do this?

Upvotes: 1

Views: 1581

Answers (2)

rici
rici

Reputation: 241721

Why is it crashing?

results.push_back(async([&]{return srs[i].run();}));

There's nothing wrong with the reference to srs in that line; as you say, it is not being modified. But the reference to i is completely meaningless; i may no longer exist when the lambda executes, and its value will be unpredictable.

But I don't understand why you feel the need to have more than one StringReturner. The class has no state, and the run method might as well be static for all the difference it makes.

What better ways are there to do this?

Do what, exactly?

Upvotes: 5

Jerry Coffin
Jerry Coffin

Reputation: 490128

If you're going to use a lambda anyway, why not just use one that directly does what you want:

#include <string>
#include <iostream>
#include <thread>
#include <future>
#include <sstream>
#include <vector>

int main() { 
    std::vector<std::future<std::string>> rets;

    auto f = []() {
        std::ostringstream b;
        b << "returned from: " << std::this_thread::get_id();
        return b.str();
    };

    for (int i=0; i<10; i++)
        rets.emplace_back(std::async(f));

    for (auto & t : rets) {
        t.wait();
        std::cout << t.get() << "\n";
    }
}

Note that the thread::id type that get_id returns is guaranteed to have an operator<<, but at least as far as I know, there's no guarantee that it's a type for which std::to_string has been overloaded.

Upvotes: 1

Related Questions