介绍
在现代软件开发中,并发编程已成为提升性能和响应性的核心需求。C++作为一门高性能系统编程语言,其原生支持的线程和互斥锁虽然强劲,但往往面临复杂性高、错误易发的问题。Async++应运而生,这是一个专为C++11设计的轻量级并发框架,由Amanieu开发,灵感来源于Microsoft的PPL(Parallel Patterns Library)和N3428 C++标准提案。
Async++的目标是简化异步任务的创建、组合和执行,让开发者能够轻松编写非阻塞、可组合的并发代码,而无需深入底层线程管理。它是一个头文件库(header-only),依赖最小,仅需C++11编译器,无需额外运行时开销。这使得它特别适合嵌入式系统、游戏引擎、高性能计算等领域。
不同于传统的多线程编程,Async++引入了“任务”(task)概念,类似于C#的Task或JavaScript的Promise。它允许开发者通过链式调用和组合操作构建复杂的工作流,同时保持代码的线性性和可读性。截至2025年10月,Async++在GitHub上已积累超过数千星标,证明了其在社区中的受欢迎程度。
本指南将带你从零起步,深入探索Async++的核心机制。通过详细的模块分类和代码示例,你将学会如何在实际项目中应用它。无论你是初学者还是资深C++开发者,这份指南都能协助你掌握异步编程的精髓。
特性
Async++以简洁、高效和可扩展为核心特性,完美契合C++的“零开销抽象”哲学。以下是其主要亮点:
1. 轻量级与零依赖
- 头文件库设计:只需包含async++.h,无需链接额外库。编译时仅需C++11支持,兼容GCC 4.7+、Clang 3.2+、MSVC 2013+等主流编译器。
- 最小开销:任务执行基于工作窃取(work-stealing)线程池,默认线程数等于CPU核心数(可通过环境变量LIBASYNC_NUM_THREADS调整)。无运行时依赖,避免了Boost.Asio等库的复杂性。
2. 任务模型的强劲表达力
- 异步任务创建:使用spawn()快速启动任务,支持lambda和函数对象。任务可返回任意类型,包括void(事件信号)。
- 链式组合:通过then()实现延续(continuation),支持值传递、任务引用和异常处理。无需手动管理回调地狱。
- 并行组合:when_all()等待所有任务完成,返回元组或向量结果;when_any()等待任一任务完成,提供索引和结果,便于实现超时或竞态场景。
- 共享任务:share()转换为shared_task,支持多次get()或then(),适用于多观察者模式,但有轻微引用计数开销。
3. 异常安全与撤销机制
- 异常传播:任务中的异常自动传播到get()或延续中,支持值基(value-based)和任务基(task-based)延续的差异化处理。
- 协作式撤销:通过cancellation_token和interruption_point()实现非强制撤销,避免资源泄漏。适合长运行任务的优雅中断。
4. 并行算法集成
- 高阶函数:parallel_invoke()并行执行多个函数;parallel_for()并行迭代范围;parallel_reduce()和parallel_map_reduce()支持并行归约,如求和或自定义聚合。
- 分区器支持:内置static_partitioner(静态分割)和auto_partitioner(动态工作窃取),优化负载均衡。
5. 可定制调度器
- 多调度器支持:默认线程池外,还提供inline_scheduler(立即执行)、thread_scheduler(每个任务新线程)和fifo_scheduler(FIFO队列)。可自定义调度器接口,仅需实现schedule(task_run_handle)。
- 等待处理器:自定义wait_handler,如UI线程禁用阻塞或模拟睡眠等待。
6. 性能优化变体
- 局部任务:local_task栈分配,无分配开销,适合高频短任务,但不支持组合。
- 事件任务:event_task手动设置值,适用于外部事件集成,如网络回调。
这些特性使Async++在基准测试中表现出色:简单任务链的开销仅为几纳秒,并行算法可线性扩展至多核CPU。相比std::future,它更注重组合性和非阻塞。
架构
Async++的架构设计精炼,围绕“任务-调度器-执行器”三层构建,确保高效性和可扩展性。核心是task<T>类,它封装了一个可能未就绪的值T,类似于future但更强劲,支持链式操作。
核心组件分类
1. 任务模块(Tasks)
- task<T>:核心类,移动唯一(move-only),单次使用。提供get()(阻塞获取结果)、wait()(阻塞等待)、ready()(轮询完成)。析构不等待。
- shared_task<T>:通过share()获取,可复制,支持多次操作,但结果拷贝而非移动。
- local_task<T>:栈上分配,高性能变体,仅支持基本等待,无组合。
- event_task<T>:手动事件任务,通过set()设置值,支持cancel()和set_exception()。
- 组合操作:when_all()和when_any()生成复合任务,返回元组/向量结果。支持任务展开(unwrapping):内部返回任务时,外层任务自动替换为内层,避免阻塞嵌套。
架构上,任务使用状态机管理:pending、completed、canceled。异常通过std::exception_ptr捕获,传播时重抛。
2. 调度器模块(Schedulers)
- 默认调度器:default_threadpool_scheduler(),工作窃取线程池。线程在首次使用时懒初始化,程序退出时销毁。
- 内置调度器:
- inline_scheduler():当前线程立即执行,适合同步上下文。
- thread_scheduler():每个任务新线程,无池化。
- fifo_scheduler:FIFO队列,支持run_all_tasks()批量执行。
- threadpool_scheduler(n):自定义线程数池。
- 自定义扩展:任何类型实现void schedule(task_run_handle t)即可。task_run_handle是可移动句柄,调用run()执行任务。支持C互操作(to_void_ptr())。
- 等待处理器:task_wait_handle接口,提供ready()轮询和on_finish()回调。线程级set_thread_wait_handler()或任务级run_with_wait_handler()设置。
调度器解耦执行与任务逻辑:spawn(sched, func)将func包装为task_run_handle,调度器异步调用其run()。
3. 并行算法模块(Parallel Algorithms)
- 范围适配器:irange(a,b)整数范围;make_range(it1,it2)迭代器范围。
- 分区器:static_partitioner(range, grain)静态分割(粒度默认范围/ (8*CPU数));auto_partitioner(range)动态窃取。to_partitioner()自动转换。
- 算法函数:parallel_invoke(funcs…)变参并行;parallel_for(range, func)并行迭代;parallel_reduce(range, init, reduce)并行归约;parallel_map_reduce(range, init, map, reduce)映射后归约。
- 架构:算法递归分割范围至分区器粒度,在线程池并行处理子范围。支持指定调度器。
4. 低级接口与宏控制
- 宏:LIBASYNC_STATIC(静态链接);LIBASYNC_NO_EXCEPTIONS(禁用异常,用abort()替换);LIBASYNC_CUSTOM_DEFAULT_SCHEDULER(自定义默认)。
- 硬件查询:hardware_concurrency()返回CPU核心数(恒定,非0)。
整体架构无循环依赖:任务持有调度器引用,调度器管理执行队列。线程安全通过原子和锁实现,强调非阻塞(优先轮询/回调)。
快速上手
上手Async++简单,只需几步:构建、集成和编写代码。
构建与安装
依赖CMake 3.1+和C++11编译器。克隆仓库后:
git clone https://github.com/Amanieu/asyncplusplus.git
cd asyncplusplus
mkdir build && cd build
cmake .. # 或 ccmake .. 配置选项
make
CMake选项:
- BUILD_SHARED_LIBS=ON:构建共享库(默认静态)。
- USE_CXX_EXCEPTIONS=OFF:禁用异常(需定义LIBASYNC_NO_EXCEPTIONS)。
- CMAKE_BUILD_TYPE=Release:优化构建。
安装后,将include目录添加到项目路径。CMake项目可include(Async++.cmake)自动链接。
基本集成
在代码中包含头文件,定义宏(如静态链接):
#define LIBASYNC_STATIC // 若静态链接
#include <async++.h>
设置线程数:export LIBASYNC_NUM_THREADS=4。
第一个示例:任务链与并行
以下是完整示例,演示任务创建、链式、组合和并行算法。输出顺序可能因调度而异。
#include <iostream>
#include <async++.h>
int main() {
// 任务1:异步执行
auto task1 = async::spawn([] {
std::cout << "任务1异步执行" << std::endl;
});
// 任务2:并行返回int
auto task2 = async::spawn([]() -> int {
std::cout << "任务2与任务1并行执行" << std::endl;
return 42;
});
// 任务3:任务2后执行,乘3
auto task3 = task2.then([](int value) -> int {
std::cout << "任务3在任务2后执行,返回值: " << value << std::endl;
return value * 3;
});
// 任务4:等待任务1和3
auto task4 = async::when_all(task1, task3);
auto task5 = task4.then([](std::tuple<async::task<void>, async::task<int>> results) {
std::cout << "任务5在任务1和3后执行。任务3返回: "
<< std::get<1>(results).get() << std::endl;
});
task5.get(); // 阻塞等待
std::cout << "任务5完成" << std::endl;
// 并行调用
async::parallel_invoke([] {
std::cout << "并行执行A..." << std::endl;
}, [] {
std::cout << "并行执行B" << std::endl;
});
// 并行for
async::parallel_for(async::irange(0, 5), [](int x) {
std::cout << x;
});
std::cout << std::endl;
// 并行归约
int sum = async::parallel_reduce({1, 2, 3, 4}, 0, [](int x, int y) {
return x + y;
});
std::cout << "求和{1,2,3,4}: " << sum << std::endl;
return 0;
}
编译:g++ -std=c++11 main.cpp -lasync++ -pthread -o app。运行输出示例:
任务1异步执行
任务2与任务1并行执行
任务3在任务2后执行,返回值: 42
任务5在任务1和3后执行。任务3返回: 126
任务5完成
并行执行A...
并行执行B
01234
求和{1,2,3,4}: 10
异常处理示例
auto risky = async::spawn([] {
throw std::runtime_error("出错了!");
});
risky.then([](int) { // 值基延续:跳过,异常传播
std::cout << "不会执行" << std::endl;
}).then([](async::task<void> parent) { // 任务基:执行,可捕获
try {
parent.get();
} catch (const std::runtime_error& e) {
std::cout << "捕获异常: " << e.what() << std::endl;
}
}).get();
撤销示例
async::cancellation_token token;
auto cancellable = async::spawn([&token] {
async::interruption_point(token); // 检查撤销
std::cout << "继续执行..." << std::endl;
// 更多工作...
});
token.cancel(); // 并发撤销
try {
cancellable.get();
} catch (const async::task_canceled&) {
std::cout << "任务已撤销" << std::endl;
}
这些示例展示了Async++的简洁性:几行代码即可实现复杂并发。
应用场景
Async++适用于任何需要异步I/O、并行计算或响应式编程的场景。其非阻塞设计特别适合实时系统。
1. 网络服务器:异步I/O处理
在高并发服务器中,使用任务链处理请求:读取→解析→响应。结合event_task集成libevent或Boost.Asio。
示例:模拟异步HTTP请求。
#include <iostream>
#include <string>
#include <async++.h>
async::task<std::string> fetch_data(const std::string& url) {
// 模拟I/O:展开内部任务
return async::spawn([url] {
std::cout << "获取数据: " << url << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟延迟
return std::string("数据从") + url;
});
}
int main() {
auto data1 = fetch_data("api1.com");
auto data2 = fetch_data("api2.com");
auto combined = async::when_all(data1, data2).then([](std::tuple<async::task<std::string>, async::task<std::string>> results) {
return std::get<0>(results).get() + " + " + std::get<1>(results).get();
});
std::cout << "合并结果: " << combined.get() << std::endl;
return 0;
}
输出:并行获取两个API,合并结果。适用于微服务或爬虫,提升吞吐量。
2. 游戏引擎:并行渲染与AI
游戏中,渲染、物理模拟和AI可并行。使用parallel_for处理粒子系统,when_all同步帧更新。
示例:并行计算粒子位置。
struct Particle { float x, y; };
void update_particles(std::vector<Particle>& particles) {
async::parallel_for(async::make_range(particles.begin(), particles.end()), [](Particle& p) {
p.x += 1.0f; // 简单更新
p.y += 0.5f;
}, async::static_partitioner(async::irange(0, particles.size()), 16)); // 粒度16
}
在60FPS游戏中,这可利用多核GPU前处理,提升帧率。
3. 数据处理管道:并行ETL
在大数据场景,parallel_map_reduce实现MapReduce-like管道:映射清洗数据,然后归约统计。
示例:并行求文件列表中单词频次。
std::vector<std::string> words = {"apple", "banana", "apple", "cherry"};
auto freq = async::parallel_map_reduce(async::irange(0, words.size()), std::map<std::string, int>{},
[&words](size_t i) { return std::make_pair(words[i], 1); },
[](std::pair<std::string, int> a, std::pair<std::string, int> b) {
a.second += b.second;
if (a.second > b.second) return a; else return b; // 简化max
return a.second > b.second ? a : b;
});
std::cout << "最高频词: " << freq.first << " (" << freq.second << ")" << std::endl;
适用于日志分析或机器学习预处理,加速数倍。
4. GUI应用:非阻塞UI
在Qt/WxWidgets中,使用自定义调度器避免UI线程阻塞。inline_scheduler处理UI更新,线程池处理后台。
示例:异步文件加载。
class UIScheduler {
public:
void schedule(async::task_run_handle t) {
// 推到UI事件循环
QMetaObject::invokeMethod(this, [t = std::move(t)]() mutable { t.run(); }, Qt::QueuedConnection);
}
};
async::UIScheduler ui_sched; // 自定义
auto load_task = async::spawn(ui_sched, [] { /* UI更新 */ });
5. 嵌入式/实时系统
local_task零分配适合RTOS;禁用异常减少开销。场景:传感器数据并行滤波。
这些场景展示了Async++的 versatility:从桌面到IoT,无缝扩展。
社区/生态
Async++社区活跃,虽规模中等,但质量高。GitHub仓库有11位贡献者,issue响应迅速。作者Amanieu(amanieu@gmail.com)积极维护,支持PR。
总结
Async++以其轻量、强劲和易用的设计,重塑了C++并发编程范式。从任务链到并行算法,它让异步代码如丝般顺滑,避免了传统线程的痛点。本指南通过结构化介绍和丰富示例,助你快速掌握其精髓。
















- 最新
- 最热
只看作者