C++11如何实现任意子线程结束后立即唤醒阻塞的主线程
问题原因
你代码的问题出在反复给同一个std::shared_future变量赋值:每次给ret赋新值时,前一个shared_future对象的引用计数会降到0触发析构。而std::future/std::shared_future的析构逻辑规定:如果其绑定的异步任务还未执行完成,析构函数会阻塞等待任务结束才会继续执行后续逻辑。
你的三次赋值顺序是先绑定90秒的任务、再绑定20秒的任务、最后绑定2秒的任务:
- 第二次赋值时,需要先释放持有的90秒任务的future,此时90秒任务还在运行,程序会阻塞等待90秒任务跑完才完成第二次赋值
- 第三次赋值时,需要释放持有的20秒任务的future,同样阻塞等待20秒任务跑完才完成第三次赋值
- 最后调用
ret.get()时等待2秒任务跑完
总耗时等于三个任务耗时之和,最终输出2 1,和你观察到的现象完全一致。
正确实现方案
C11标准库没有直接提供等待任意future就绪的接口,你可以使用以下兼容C11的方案实现需求:
实现思路
用条件变量+共享标记实现,每个子线程执行完成后主动修改共享标记、触发条件变量通知,主线程只需要等待条件变量的通知即可拿到第一个完成的任务结果。
代码示例
#include <iostream> #include <thread> #include <mutex> #include <condition_variable> #include <vector> std::pair<size_t, size_t> fun(size_t i, size_t j) { std::this_thread::sleep_for(std::chrono::seconds(i * j)); return { i, j }; } int main() { std::mutex mtx; std::condition_variable cv; std::pair<size_t, size_t> result; bool result_ready = false; // 批量创建子线程 std::vector<std::thread> threads; threads.emplace_back([&](){ auto res = fun(10,9); std::lock_guard<std::mutex> lock(mtx); if(!result_ready) { // 只保留第一个完成的任务结果 result = res; result_ready = true; cv.notify_one(); } }); threads.emplace_back([&](){ auto res = fun(5,4); std::lock_guard<std::mutex> lock(mtx); if(!result_ready) { result = res; result_ready = true; cv.notify_one(); } }); threads.emplace_back([&](){ auto res = fun(2,1); std::lock_guard<std::mutex> lock(mtx); if(!result_ready) { result = res; result_ready = true; cv.notify_one(); } }); // 主线程阻塞等待第一个结果返回 std::unique_lock<std::mutex> lock(mtx); cv.wait(lock, [&](){ return result_ready; }); // 输出结果 std::cout << result.first << "\t" << result.second << "\n"; // 按需选择:不需要等待剩余线程完成就调用detach,需要等待就调用join for(auto& t : threads) { t.detach(); } return 0; }
该代码运行后仅需等待2秒就会输出2 1,符合预期需求。如果可以使用C++20标准,也可以直接调用标准库提供的std::when_any接口实现相同逻辑,代码会更简洁。
内容的提问来源于stack exchange,提问作者dy66
相关产品推荐
相关产品推荐

