C++多线程join首个完成线程即返回无需等待其余线程实现方法
问题说明
需求:启动2个或多个线程执行任务,仅获取最先执行完成的线程返回结果,首个线程执行结束后主线程立刻继续向下运行,无需等待剩余线程执行完毕。
原有实现的缺陷:循环对所有线程调用join()会一对一阻塞等待每个线程执行结束,必然要等所有线程跑完才会进入后续逻辑,不符合需求。原有问题代码如下:
#include <thread> #include <vector> #include <iostream> #include <functional> int main() { std::vector<std::thread> vecOfThreads; std::function<void()> func = []() { // 内部任务逻辑 std::cout << "From Thread ID : " << std::this_thread::get_id() << "\n"; }; vecOfThreads.push_back(std::thread(func)); std::thread th1(func); std::thread th2(func); vecOfThreads.push_back(std::move(th1)); vecOfThreads.push_back(std::move(th2)); for (std::thread& th : vecOfThreads) { if (th.joinable()) th.join(); } return 0; }
最优实现方案
std::thread::join()本身是一对一阻塞等待的接口,没有原生提供“等待任意一个线程完成”的能力,需要通过线程间同步机制实现需求,优先选择无CPU忙等、无资源泄漏的实现,核心逻辑如下:
- 用同步标记记录是否已有线程完成任务,避免多线程数据竞争
- 用条件变量让主线程阻塞等待,避免轮询占用CPU资源
- 首个完成任务的线程更新标记后,通过条件变量唤醒主线程
- 主线程被唤醒后即可继续执行后续业务逻辑,同时向其余运行中的线程发送终止信号,回收线程资源,避免悬空引用、线程泄漏问题
C++11 通用兼容版本
适用于所有支持C++11标准的编译环境,兼容性最好:
#include <thread> #include <vector> #include <iostream> #include <mutex> #include <condition_variable> #include <atomic> #include <chrono> int main() { std::mutex mtx; std::condition_variable cv; std::atomic<bool> taskDone{false}; // 可根据实际业务替换为对应返回值类型,示例用线程ID作为结果 std::thread::id firstThreadRes; auto task = [&]() { // 替换为实际业务逻辑,这里用随机休眠模拟不同任务耗时 std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 1000)); // 原子操作抢占完成标记,只有第一个完成的线程能写入结果 bool expected = false; if (taskDone.compare_exchange_strong(expected, true)) { { std::lock_guard<std::mutex> lock(mtx); firstThreadRes = std::this_thread::get_id(); } cv.notify_one(); } // 未抢到标记的线程可直接终止后续逻辑,自行退出 return; }; std::vector<std::thread> vecOfThreads; // 启动2个任务线程 for (int i = 0; i < 2; i++) { vecOfThreads.emplace_back(task); } // 主线程阻塞等待首个线程完成通知 std::unique_lock<std::mutex> lock(mtx); cv.wait(lock, [&](){ return taskDone.load(); }); // 此处已拿到首个线程的执行结果,可直接执行后续业务逻辑 std::cout << "最先执行完成的线程ID: " << firstThreadRes << "\n"; std::cout << "主线程继续向下执行,无需等待剩余线程\n"; // 回收所有线程资源:已完成的线程join会立刻返回,未完成的线程等待其退出后返回 for (auto& th : vecOfThreads) { if (th.joinable()) { th.join(); } } return 0; }
注意:如果任务逻辑是无法中途中断的阻塞操作(比如阻塞式网络IO),拿到首个结果后可以对剩余线程做
detach()处理,但必须保证线程访问的所有变量生命周期长于线程运行周期,避免触发未定义行为。
C++20 简化版本
如果编译环境支持C++20标准,可以用std::jthread自带的停止令牌实现更简洁的逻辑,无需手动维护原子标记:
#include <thread> #include <vector> #include <iostream> #include <mutex> #include <condition_variable> #include <chrono> int main() { std::mutex mtx; std::condition_variable cv; bool taskDone = false; std::thread::id firstThreadRes; auto task = [&](std::stop_token st) { // 模拟不同耗时的业务逻辑 std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 1000)); if (st.stop_requested()) return; // 收到停止信号直接退出 { std::lock_guard<std::mutex> lock(mtx); if (taskDone) return; // 双重检查避免重复通知 taskDone = true; firstThreadRes = std::this_thread::get_id(); } cv.notify_one(); }; std::vector<std::jthread> vecOfThreads; for (int i = 0; i < 2; i++) { vecOfThreads.emplace_back(task); } std::unique_lock<std::mutex> lock(mtx); cv.wait(lock, [&](){ return taskDone; }); // 拿到结果后向所有线程发送停止信号,jthread析构时会自动join回收资源,无需手动处理 for (auto& jth : vecOfThreads) { jth.request_stop(); } std::cout << "最先执行完成的线程ID: " << firstThreadRes << "\n"; std::cout << "主线程继续向下执行,无需等待剩余线程\n"; return 0; }
内容的提问来源于stack exchange,提问作者InUser
相关产品推荐
相关产品推荐

