如何等待任意C++异步任务执行完成并获取其原始启动索引
解决方案
你可以通过线程安全完成队列实现需求,该方案性能开销极低,不需要修改原有do_something的入参与返回值,也不需要每次任务完成时遍历整个future列表,完全符合你的要求。
步骤1:实现线程安全队列
如果对性能要求极高,可直接替换为工业级无锁队列,以下是基础的线程安全队列实现:
#include <queue> #include <mutex> #include <condition_variable> template<typename T> class ThreadSafeQueue { private: std::queue<T> m_queue; std::mutex m_mutex; std::condition_variable m_cv; public: void push(T value) { std::lock_guard<std::mutex> lock(m_mutex); m_queue.push(std::move(value)); m_cv.notify_one(); } T pop() { std::unique_lock<std::mutex> lock(m_mutex); m_cv.wait(lock, [this] { return !m_queue.empty(); }); T res = std::move(m_queue.front()); m_queue.pop(); return res; } };
步骤2:修改业务代码
仅需要在提交异步任务时用lambda做一层轻量包装,捕获任务下标即可,不需要修改do_something的任何逻辑:
// 队列存储pair:第一个元素为任务启动的原始下标,第二个为do_something返回值 ThreadSafeQueue<std::pair<int, int>> comp_queue; std::vector<std::future<void>> asyncs; for (int i = 0; i < 4; ++i) { asyncs.push_back(std::async( std::launch::async, [i, &comp_queue] { int ret = do_something(); comp_queue.push({i, ret}); } )); } // 按任务完成顺序读取结果,每次操作为O(1),无遍历开销 for (int i = 0; i < 4; ++i) { auto [task_idx, wait_time] = comp_queue.pop(); std::cout << "Task " << task_idx << " Wait time: " << wait_time << std::endl; }
方案优势
- 性能损耗极低:仅在任务完成和结果读取时各有一次队列操作,无锁队列实现下额外开销可忽略
- 无需修改原有业务逻辑:
do_something不需要接收索引参数,也不需要返回带索引的tuple - 天然支持按完成顺序输出结果,同时可获取任务的原始启动下标
内容的提问来源于stack exchange,提问作者CyanCoding
相关产品推荐
相关产品推荐

