C++ 中实现 `Task::WhenAll` 和 `Task::WhenAny` 的两种方案

📚 C++ 中实现 Task::WhenAllTask::WhenAny 的两种方案

引用

  1. 拈朵微笑的花 想一番人世變換 到頭來輸贏又何妨
  2. 日與夜互消長 富與貴難久長 今早的容顏老於昨晚
  3. C++ 标准库异步编程示例(一)
  4. C++ TAP(基于任务的异步编程模式)

🚀 引言:异步编程的需求与挑战

在现代软件开发中,异步编程已成为提升应用性能的关键技术。C# 提供了优雅的 Task.WhenAllTask.WhenAny 机制来管理多个异步任务,但 C++ 标准库中缺乏直接等效功能。本文将深入探讨两种高效实现方案:

异步编程需求
WhenAll 实现
WhenAny 实现
简单轮询方案
高效条件变量方案
简单轮询方案
高效条件变量方案

🔄 方案1:基于轮询的简单实现

🛠️ when_all 实现(简单轮询)

#include <vector>
#include <future>template <typename T>
std::vector<T> when_all(std::vector<std::future<T>>& futures) {std::vector<T> results;for (auto& fut : futures) {results.push_back(fut.get());}return results;
}// void 特化版本
void when_all(std::vector<std::future<void>>& futures) {for (auto& fut : futures) {fut.get();}
}
原理说明
  • 顺序执行:循环遍历每个 future,调用 get() 方法阻塞等待结果
  • 结果收集:对于非 void 任务,结果存储在 vector 中返回
  • 优点:实现简单,代码直观
  • 缺点:顺序等待导致性能瓶颈

🛠️ when_any 实现(轮询方式)

#include <chrono>
#include <vector>
#include <future>template <typename T>
size_t when_any(std::vector<std::future<T>>& futures) {while (true) {for (size_t i = 0; i < futures.size(); ++i) {if (futures[i].wait_for(std::chrono::seconds(0)) == std::future_status::ready) {return i;}}std::this_thread::sleep_for(std::chrono::milliseconds(10));}
}
原理说明
  • 轮询检测:使用 wait_for(0) 非阻塞检查任务状态
  • 指数退避:每次检测后休眠减少 CPU 占用
  • 返回索引:返回第一个完成任务的索引
  • 缺点:CPU 占用高,响应延迟(最大10ms)
开始
遍历所有future
是否ready?
返回索引
休眠10ms

⚡ 方案2:基于条件变量的高效实现

🧩 WhenAll 类设计(高效等待所有任务)

#include <vector>
#include <future>
#include <mutex>
#include <condition_variable>
#include <thread>class WhenAll {
public:void add_future(std::future<void> fut) {std::lock_guard<std::mutex> lock(mtx);futures.push_back(std::move(fut));count++;}void wait() {std::unique_lock<std::mutex> lock(mtx);if (count == 0) return;for (auto& fut : futures) {std::thread([&, this] {fut.wait();std::lock_guard<std::mutex> lock(mtx);if (--count == 0) cv.notify_all();}).detach();}cv.wait(lock, [this] { return count == 0; });}private:std::vector<std::future<void>> futures;std::mutex mtx;std::condition_variable cv;int count = 0;
};
架构解析
WhenAll
- futures: vector>
- mtx: mutex
- cv: condition_variable
- count: int
+add_future(future fut)
+wait()
工作原理
  1. 添加任务:通过 add_future 添加异步任务
  2. 监控线程:为每个任务创建监控线程
  3. 条件等待:主线程等待条件变量 cv
  4. 完成通知:最后完成的任务触发 notify_all()
  5. 资源释放:监控线程自动分离(detach
性能优势
  • 零轮询:完全消除CPU空转
  • 即时响应:任务完成立即通知
  • 线程安全:互斥锁保护共享状态

🧩 WhenAny 类设计(高效等待任意任务)

#include <vector>
#include <future>
#include <mutex>
#include <condition_variable>template <typename T>
class WhenAny {
public:template <typename Func>void add_task(Func func) {std::lock_guard<std::mutex> lock(mtx);futures.push_back(std::async(std::launch::async, [this, func] {auto result = func();{std::lock_guard<std::mutex> lock(mtx);if (!completed) {completed = true;completed_index = futures.size() - 1;cv.notify_all();}}return result;}));}size_t wait() {std::unique_lock<std::mutex> lock(mtx);cv.wait(lock, [this] { return completed; });return completed_index;}std::future<T>& get_future(size_t index) {return futures[index];}private:std::vector<std::future<T>> futures;std::mutex mtx;std::condition_variable cv;bool completed = false;size_t completed_index = 0;
};
架构解析
WhenAny
- futures: vector>
- mtx: mutex
- cv: condition_variable
- completed: bool
- completed_index: size_t
+add_task(Func func)
+wait()
+get_future(size_t index)
工作流程
主线程任务线程WhenAny对象add_task(任务1)启动异步任务add_task(任务2)启动异步任务wait()任务完成返回完成索引主线程任务线程WhenAny对象
关键特性
  1. 泛型支持:模板化设计支持任意返回类型
  2. 一次性通知completed 标志确保只通知一次
  3. 结果获取get_future 方法获取已完成任务的结果
  4. 线程安全:互斥锁保护共享状态

🧪 使用示例与场景分析

基本使用示例

#include <iostream>
#include <chrono>
#include <thread>int main() {// 示例1: WhenAll 使用WhenAll wa;wa.add_future(std::async([] { std::this_thread::sleep_for(std::chrono::seconds(1));std::cout << "任务1完成\n";}));wa.add_future(std::async([] { std::this_thread::sleep_for(std::chrono::seconds(2));std::cout << "任务2完成\n"; }));std::cout << "等待所有任务...\n";wa.wait();std::cout << "所有任务完成!\n";// 示例2: WhenAny 使用WhenAny<int> wany;wany.add_task([] {std::this_thread::sleep_for(std::chrono::seconds(3));return 100;});wany.add_task([] {std::this_thread::sleep_for(std::chrono::seconds(1));return 200;});std::cout << "等待任意任务完成...\n";size_t index = wany.wait();auto& fut = wany.get_future(index);std::cout << "任务" << index << "最先完成,结果: " << fut.get() << "\n";return 0;
}

实际应用场景

  1. 微服务聚合:并行调用多个微服务,等待所有响应
  2. 竞态条件处理:多个数据源查询,使用第一个返回结果
  3. 资源加载:并行加载多个资源文件,等待全部完成
  4. 超时处理:多个备用服务调用,使用最先响应的服务

🚀 高级优化与扩展

性能优化技术

  1. 线程池集成:避免为每个任务创建新线程

    void wait(ThreadPool& pool) {std::unique_lock<std::mutex> lock(mtx);if (count == 0) return;for (auto& fut : futures) {pool.enqueue([&, this] {fut.wait();std::lock_guard<std::mutex> lock(mtx);if (--count == 0) cv.notify_all();});}cv.wait(lock, [this] { return count == 0; });
    }
    
  2. 共享future优化:避免多次get()调用

    void add_future(std::shared_future<void> shared_fut) {std::lock_guard<std::mutex> lock(mtx);shared_futures.push_back(shared_fut);count++;
    }
    
  3. 批量任务添加:减少锁竞争

    void add_futures(const std::vector<std::future<void>>& new_futures) {std::lock_guard<std::mutex> lock(mtx);futures.insert(futures.end(), new_futures.begin(), new_futures.end());count += new_futures.size();
    }
    

功能扩展

  1. 超时支持:添加 wait_forwait_until

    template <typename Rep, typename Period>
    bool wait_for(const std::chrono::duration<Rep, Period>& timeout) {std::unique_lock<std::mutex> lock(mtx);return cv.wait_for(lock, timeout, [this] { return count == 0; });
    }
    
  2. 混合类型支持:使用 std::variant

    class WhenAnyVariant {
    public:template <typename Func>void add_task(Func func) {using ResultType = decltype(func());std::lock_guard<std::mutex> lock(mtx);futures.push_back(std::async(std::launch::async, [=] {auto result = func();{std::lock_guard<std::mutex> lock(mtx);if (!completed) {completed = true;completed_index = futures.size() - 1;cv.notify_all();}}return std::variant<ResultType>(std::move(result));}));}// ... 其他成员
    private:std::vector<std::future<std::variant<int, double, std::string>>> futures;
    };
    
  3. 进度追踪:添加进度回调

    void wait(std::function<void(int)> progress_callback) {std::unique_lock<std::mutex> lock(mtx);if (count == 0) return;for (auto& fut : futures) {std::thread([&, this] {fut.wait();std::lock_guard<std::mutex> lock(mtx);progress_callback(++completed_count);if (completed_count == count) cv.notify_all();}).detach();}cv.wait(lock, [this] { return completed_count == count; });
    }
    

⚠️ 注意事项与最佳实践

异常处理

void add_future(std::future<void> fut) {std::lock_guard<std::mutex> lock(mtx);futures.push_back(std::async(std::launch::async, [fut = std::move(fut)]() mutable {try {fut.get();} catch (const std::exception& e) {std::cerr << "任务异常: " << e.what() << std::endl;}}));count++;
}

资源管理最佳实践

  1. 避免线程泄漏:使用RAII包装线程
  2. 预防死锁:锁粒度最小化
  3. 内存安全:使用智能指针管理共享数据
  4. 性能监控:添加任务执行时间统计

平台适配性

  1. 跨平台考虑:使用标准库确保可移植性
  2. 编译器支持:确保C++17及以上特性
  3. 异步模型:与平台特定API(如IOCP/epoll)集成

📊 性能对比分析

方案类型CPU占用响应延迟内存开销适用场景
简单轮询高 (持续10-100%)高 (10ms级)任务少、低频率
条件变量低 (<1%)低 (µs级)高并发、实时系统
线程池集成中 (可控)低 (µs级)大规模任务处理

在这里插入图片描述

🔮 未来发展与C++标准展望

C++23/26异步特性

  1. std::execution:标准执行器支持
  2. 协程增强:更简洁的异步代码编写
  3. 网络库集成:与标准网络库协同工作

社区解决方案

  1. Boost.Asio:提供 async_wait_all 等实用工具
  2. Folly库:Facebook的高性能异步工具集
  3. Qt Concurrent:跨平台异步框架

💎 总结

本文详细探讨了在C++中实现 Task.WhenAllTask.WhenAny 的两种核心方案:

  1. 简单轮询方案:适用于轻量级场景,实现简单但效率较低
  2. 条件变量方案:高性能实现,适用于生产环境
    • 零轮询设计减少CPU占用
    • 即时响应确保最佳性能
    • 扩展性强,支持超时、进度回调等高级功能
少量任务
大量任务
高实时性
可接受延迟
选择实现方案
任务数量
简单轮询方案
实时性要求
条件变量方案
线程池集成方案

最佳实践建议

  • 小型工具类使用简单轮询方案
  • 高性能服务器使用条件变量方案
  • 大规模并行处理集成线程池
  • 始终考虑异常安全和资源管理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/90764.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/90764.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【学习】Codeforces Global Round 15 C. Maximize the Intersections

题意&#xff1a;给出一个圆&#xff0c;顺时针排布1~2*n&#xff0c;已知连了k条边&#xff0c;问这个圆最好情况下有多少个线的交点&#xff0c;要求线与线之间不能有重复的连接点&#xff0c;也就是每个点只能被一条线连接 思路&#xff1a; 1.考虑没有线的时候&#xff0…

图论:Dijkstra算法

昨天介绍了最小生成树的两个算法&#xff0c;最小生成树的两个算法旨在求解无向有权图中的最小代价联通图的问题&#xff0c;那么对于有向有权图&#xff0c;从起点到终点的最小花费代价问题就可以用 Dijkstra 算法来解决而且Dijkstra算法可以求出来从起始点开始到所有节点的最…

WPFC#超市管理系统(2)顾客管理、供应商管理、用户管理

超市管理系统3. 顾客管理3.1 顾客新增3.2 DataGrid样式3.3 顾客删除3.4 顾客修改4. 供应商管理4.1 供应商管理主界面4.2 新增供应商4.3 修改供应商5. 用户管理5.1 用户管理主界面5.2 新增用户5.3 修改用户总结3. 顾客管理 在CustomerView.xaml使用命令绑定方式添加页面加载Loa…

Windows本地部署DeepSeek

1、Ollama1、下载Ollama安装包https://ollama.com/download&#xff08;如果下载很慢 可以直接找我拿安装包&#xff09;2、使用命令行安装打开cmd 将下载的安装包OllamaSetup.exe 放到想要安装的目录下。&#xff08;如果直接双击&#xff0c;会装到C盘&#xff09;例如想装到…

基于Python的新闻爬虫:实时追踪行业动态

引言 在信息时代&#xff0c;行业动态瞬息万变。金融从业者需要实时了解政策变化&#xff0c;科技公司需要跟踪技术趋势&#xff0c;市场营销人员需要掌握竞品动向。传统的人工信息收集方式效率低下&#xff0c;难以满足实时性需求。Python爬虫技术为解决这一问题提供了高效方…

阿里视频直播解决方案VS(MediaMTX + WebRTC) 流媒体解决方案

背景&#xff1a; 公司采购了新的摄像头&#xff0c;通过rtsp或者rtmp推流到云平台&#xff0c;云平台内部进行转码处理&#xff0c;客户端使用HLS或HTTP-FLV播放&#xff0c;移动App可能使用HLS或私有SDK&#xff0c;超低延时则采用WebRTC。 技术选型&#xff1a; RTSP&…

day33:零基础学嵌入式之网络——TCP并发服务器

一、服务器1.服务器分类单循环服务器&#xff1a;只能处理一个客户端任务的服务器并发服务器&#xff1a;可同时处理多个客户端任务的服务器二、TCP并发服务器的构建1.如何构建&#xff1f;&#xff08;1&#xff09;多进程&#xff08;每一次创建都非常耗时耗空间&#xff0c;…

VR全景制作的流程?VR全景制作可以用在哪些领域?

VR全景制作的流程&#xff1f;VR全景制作可以用在哪些领域&#xff1f;VR全景制作&#xff1a;流程、应用与未来虚拟现实&#xff08;VR&#xff09;全景制作正迅速改变我们的感官体验&#xff0c;使我们能够身临其境地探索虚拟世界&#xff0c;享受沉浸式的奇妙感受。那么&…

用LangChain重构客服系统:腾讯云向量数据库+GPT-4o实战

人们眼中的天才之所以卓越非凡&#xff0c;并非天资超人一等而是付出了持续不断的努力。1万小时的锤炼是任何人从平凡变成超凡的必要条件。———— 马尔科姆格拉德威尔 目录 一、传统客服系统痛点与重构价值 1.1 传统方案瓶颈分析 1.2 新方案技术突破点 二、系统架构设计&…

主要分布在腹侧海马体(vHPC)CA1区域(vCA1)的混合调谐细胞(mixed-tuning cells)对NLP中的深层语义分析的积极影响和启示

腹侧海马体CA1区&#xff08;vCA1&#xff09;的混合调谐细胞&#xff08;mixed-tuning cells&#xff09;通过整合情感、社会关系、空间概念等多模态信息&#xff0c;形成动态的情景化语义表征&#xff0c;为自然语言处理&#xff08;NLP&#xff09;的深层语义分析提供了重要…

ESP32的ADF详解:6. Audio Processing的API

一、Downmix 1. 核心功能 将基础音频流和新加入音频流混合为单一输出流&#xff0c;支持动态增益控制和状态转换。输出声道数与基础音频一致&#xff0c;新加入音频自动转换声道匹配。2. 关键特性声道处理 输出声道数 基础音频声道数新加入音频自动转换声道&#xff08;如立体…

Qt(基本组件和基本窗口类)

一、基本组件1. Designer设计师为什么要上来先将这个东西呢&#xff0c;这个是QT外置的设计界面的工具&#xff0c;没啥用&#xff0c;所以了解一下。我们用的多的是QT内置的界面设计&#xff0c;只需要我们双击我们创建的项目的.ui文件就可以进入这个界面&#xff0c;你对界面…

docker与k8s的容器数据卷

Docker容器数据卷 特性 docker镜像由多个只读层叠加而成&#xff0c;启动容器时&#xff0c;Docker会加载只读镜像层并在镜像栈顶部添加一个读写层。如果运行中的容器修改了现有的一个已经存在的文件&#xff0c;那么该文件将会从读写层下面的只读层复制到读写层&#xff0c;该…

自然语言处理技术应用领域深度解析:从理论到实践的全面探索

1. 引言:自然语言处理的技术革命与应用前景 自然语言处理(Natural Language Processing,NLP)作为人工智能领域的核心分支,正在以前所未有的速度改变着我们的数字化生活。从最初的规则基础系统到如今基于深度学习的大语言模型,NLP技术经历了从理论探索到实际应用的深刻变…

OpenGLRender开发记录(二): 阴影(shadowMap,PCF,PCSS)

目录已实现功能阴影shadowMapPCFPCSS实现shadowMapPCFPCSS阴影GitHub主页&#xff1a;https://github.com/sdpyy1 OpenGLRender:https://github.com/sdpyy1/CppLearn/tree/main/OpenGL 已实现功能 除了上次实现IBL之外&#xff0c;项目目前新增了imGUI的渲染&#xff0c;更方便…

Linux:日志乱码

1、Linux日志乱码可能是XShell客户端编码没设置为UTF-8引起的&#xff0c;按照以下步骤&#xff0c;设置终端格式&#xff1a;中文版&#xff1a;打开Xshell会话属性&#xff08;文件→属性→终端→编码&#xff09;&#xff0c;选择与服务器一致的编码格式&#xff08;如UTF-8…

Rouge:面向摘要自动评估的召回导向型指标——原理、演进与应用全景

“以n-gram重叠量化文本生成质量&#xff0c;为摘要评估提供可计算标尺” Rouge&#xff08;Recall-Oriented Understudy for Gisting Evaluation&#xff09; 是由 南加州大学信息科学研究所&#xff08;ISI&#xff09;的Chin-Yew Lin 于2004年提出的自动文本摘要评估指标&am…

[STM32][HAL]stm32wbxx 超声波测距模块实现(HY-SRF05)

前言 在电子技术应用中,距离测量是一个常见且重要的需求。超声波模块因其测量精度较高、成本较低、易于使用等优点,被广泛应用于机器人避障、液位检测、智能停车系统等领域。该文主要讲解以stm32wb芯片为主控,用HAL库来对HY-SRF05超声波模块进行代码编写,实现基本的驱动和测…

MySQL 性能调优实战指南:从诊断到优化全解析

引言在日常的数据库运维工作中&#xff0c;我们经常需要对 MySQL 数据库进行诊断和性能分析。本文将介绍一套全面的 MySQL 诊断脚本&#xff0c;适用于 MySQL 8.0&#xff08;兼容 8.0.15 及以上版本&#xff09;&#xff0c;涵盖事务锁分析、性能瓶颈定位、配置检查、连接状态…

8. 状态模式

目录一、应用背景二、状态模式2.1 解决的问题2.2 角色2.3 实现步骤三、通用设计类图四、实现4.1 设计类图4.2 状态转换图4.3 代码实现一、应用背景 某对象发生变化时&#xff0c;其所能做的操作也随之变化。应用程序的可维护性和重用性差代码的逻辑较复杂 二、状态模式 2.1 …