C++使用std::async创建Futures向量时触发访问违规问题
问题分析与修复
核心问题:悬空引用触发未定义行为
你的代码中,ConstructTask函数通过std::cref(units)将局部变量units的引用传递给std::async的异步任务,但units是ConstructTask的局部对象——函数执行完毕返回后,该局部对象会立即销毁。而异步任务可能在ConstructTask返回后才启动执行,此时引用已变为悬空状态,访问该引用会触发未定义行为,表现为调试器抛出异常。
修复方案
移除std::cref,直接传递units的值,让异步任务获得独立的对象副本,彻底避免引用悬空问题:std::async会自动拷贝参数传递给异步函数,传递值后,异步任务将持有units的独立副本,即使原局部对象销毁,副本依然有效。
修正后的代码
#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}}; // 移除std::cref,直接传递units的值 return std::async(std::launch::async, Task::Create, 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; } }
额外优化建议
如果实际场景中units对象体积较大、拷贝成本高,可以采用两种替代方案:
- 在
ConstructTask中用std::make_shared创建units的共享副本,传递std::shared_ptr给异步任务,确保对象生命周期覆盖异步任务执行全程。 - 将
units的创建逻辑移到异步任务内部,避免跨线程传递大对象或引用。
内容的提问来源于stack exchange,提问作者Bilentor
相关产品推荐
相关产品推荐

