You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 18:21:52