I am trying to do some multithreading with futures in C++ using std::async. I need to create a bunch of objects whose constructors do some serializations in parallel. I am calling a function that calls the constructor and returns a shared_ptr to the object. I call this function with std::async and create a vector of futures returned by this function and then call .get() on each future in a loop where I assumed it will be a blocking call but that’s where I get an exception in the debugger. I am not sure what’s going on.
Here is a simplified version of a reproducible example that throws exception with MSVC 14.3+ when compiled with C++17
#include <iostream>
#include <vector>
#include <future>
#include <memory>
#include <numeric>
struct TaskUnit {
std::vector<int> preds;
int vertex;
};
struct Task {
std::vector<TaskUnit> units;
int task_id;
Task(std::vector<TaskUnit> const& units, int const task_id)
: units(units), task_id(task_id) {}
static std::shared_ptr<Task const>
Create(std::vector<TaskUnit> const& units, int const id)
{
return std::make_shared<Task const>(units, id);
}
};
auto ConstructTask(int const task_count)
{
std::vector<TaskUnit> units{ {{1,2}, 1}};
return std::async(std::launch::async, Task::Create, std::cref(units), task_count);
}
auto ConstructTasks(int const n_tasks)
{
std::vector<std::future<std::shared_ptr<Task const>>> futures;
std::vector<int> range(n_tasks);
std::iota(range.begin(), range.end(), 0);
for (auto const& task_count : range)
{
futures.push_back(ConstructTask(task_count));
}
std::vector<std::shared_ptr<Task const>> tasks;
for (auto& future : futures)
{
tasks.push_back(future.get());
}
return tasks;
}
int main()
{
auto tasks = ConstructTasks(10);
for (auto const& task : tasks)
{
std::cout << task->task_id;
}
}
>Solution :
You pass a reference to a std::vector<TaskUnit> that goes out of scope and dies when the function returns. Pass it by value instead:
class Task {
static std::shared_ptr<Task const>
Create(std::vector<TaskUnit> const& units, int const id)
// ^ remove
{
return std::make_shared<Task const>(units, id);
}
};
auto ConstructTask(int const task_count) {
std::vector<TaskUnit> units{{{1, 2}, 1}};
return std::async(std::launch::async, Task::Create, units, task_count);
// ^^^^^ remove cref
}