nexec 是一个 C++20 异步执行框架,基于 sender/receiver 模型,并提供与 C++20 协程的桥接。概念上对齐 P2300 (std::execution) 的一部分设计,覆盖常用适配器与调度能力,适合在生产或学习场景中组合异步任务。
find_package(nexec CONFIG REQUIRED)
target_link_libraries(your_target PRIVATE nexec::nexec)源码安装示例:
cmake -S . -B build -DNEXEC_BUILD_TESTS=OFF -DNEXEC_BUILD_EXAMPLES=OFF
cmake --build build
cmake --install build --prefix /path/to/prefix
# 消费方: -DCMAKE_PREFIX_PATH=/path/to/prefixinclude(FetchContent)
FetchContent_Declare(
nexec
GIT_REPOSITORY https://github.com/wkcs/nexec.git
GIT_TAG v1.0.0 # 或某个 commit
)
FetchContent_MakeAvailable(nexec)
target_link_libraries(your_target PRIVATE nexec::nexec)或:
add_subdirectory(third_party/nexec)
target_link_libraries(your_target PRIVATE nexec::nexec)作为子项目引入时,默认不编译 examples / tests。
本仓库提供 overlay port,可在未合入官方 registry 前本地安装:
# 在任意使用 vcpkg 的工程中
vcpkg install nexec --overlay-ports=/path/to/nexec/ports
# 或在工程根目录放置 / 引用本仓库的 vcpkg-configuration.json:
# { "overlay-ports": ["/path/to/nexec/ports"] }然后:
find_package(nexec CONFIG REQUIRED)
target_link_libraries(your_target PRIVATE nexec::nexec)可选 feature:nexec[uring](强制 Linux io_uring 后端,依赖 liburing)。
头文件用法:
#include <nexec/execution.hpp>┌────────────┐ Connect ┌──────────────────┐ Start ┌──────────────┐
│ Sender │ ─────────────→ │ OperationState │ ────────────→ │ Receiver │
│ (描述任务) │ │ (执行中的任务) │ │ (接收结果) │
└────────────┘ └──────────────────┘ └──────────────┘
↓ SetValue
↓ SetError
↓ SetStopped
- Sender(发送器):描述一个异步操作,但不立即执行
- Receiver(接收器):接收异步操作的完成信号(值/错误/停止)
- OperationState(操作状态):Connect(sender, receiver) 的结果,调用 Start() 后开始执行
- 调用
Start(op)后,调用方必须让OperationState、receiver 和其捕获的引用一直存活到收到一次完成信号。SyncWait会在等待期间持有 operation state;手写Connect/Start时由调用方负责。 - 线程池 scheduler 是非拥有句柄。持有它的线程池必须在所有使用该 scheduler 的 sender、operation state 和后台任务完成后才析构。
SyncWait(sender)的语义为:成功时返回有值的std::optional;sender 以 stopped 完成时返回空std::optional;错误会重新抛出。AsyncScope析构不会 join 任务。若后续要销毁任务所依赖的 scheduler 或外部资源,请先SyncWait(scope.OnEmpty()),或在协程中co_await scope.OnEmpty()。Spawn未处理的set_error会std::terminate()。AsyncMutex的等待队列当前不支持取消:等待Lock或LockScoped的协程会一直等待到获得锁。StartDetached不会延长用户捕获对象或 scheduler 的寿命;后台任务所引用的资源必须自行保持有效。未处理的set_error会std::terminate()(与 P2300 一致);需吞错时请先LetError。- 线程池
Enqueue在池已停止时返回失败;库内所有完成/唤醒路径必须兜底(同步执行或同步resume),禁止静默丢弃回调。 split多播要求完成值可拷贝;Task/多数operation state 在Start后不可移动。
详细的 Wiki 文档位于 docs/wiki/,包含核心概念、全部组件用法、线程池、协程、同步原语、I/O 子系统、构建配置、C++20 Modules 与测试覆盖率等主题。设计文档见 docs/design_document.md。
nexec/
├── CMakeLists.txt # CMake 构建与安装导出
├── LICENSE # MIT
├── ports/nexec/ # vcpkg overlay port
├── include/nexec/
│ ├── core.hpp # 核心类型和概念定义
│ ├── just.hpp # Just/JustError/JustStopped 发送器工厂
│ ├── then.hpp # Then 发送器适配器
│ ├── when_all.hpp # WhenAll 并行组合器
│ ├── sync_wait.hpp # SyncWait 同步等待
│ ├── thread_pool.hpp # 线程池调度器
│ ├── async_scope.hpp # 后台任务作用域及 OnEmpty 屏障
│ ├── mutex.hpp / semaphore.hpp # 异步互斥锁与信号量
│ ├── coroutine.hpp # C++20 协程集成
│ └── execution.hpp # 主头文件(包含所有组件)
├── modules/
│ └── nexec.cppm # C++20 named module(import nexec)
└── examples/
├── basic.cpp
├── pipeline.cpp
├── thread_pool.cpp
├── when_all.cpp
├── coroutine.cpp
├── adapters_demo.cpp
├── production_demo.cpp
└── module_basic.cpp # 需 NEXEC_ENABLE_MODULES
# 需要支持 C++20 与协程的编译器(Clang 14+, GCC 12+, MSVC 2022+)
cmake -S . -B build
cmake --build build
# 运行示例(顶层构建默认开启 NEXEC_BUILD_EXAMPLES)
./build/example_basic
./build/example_pipeline
./build/example_thread_pool
./build/example_when_all
./build/example_coroutine
./build/example_adapters_demo
./build/example_production_demo常用选项:
| 选项 | 默认(顶层) | 说明 |
|---|---|---|
NEXEC_BUILD_EXAMPLES |
ON | 编译 examples |
NEXEC_BUILD_TESTS |
ON | 编译单元测试 |
NEXEC_IO_BACKEND |
auto |
auto / thread_pool / uring |
NEXEC_ENABLE_MODULES |
OFF | C++20 modules(需 CMake ≥ 3.28 + Ninja) |
cmake -B build -DNEXEC_BUILD_TESTS=ON
cmake --build build --target nexec_tests
ctest --test-dir build --output-on-failure测试框架为 GoogleTest(CMake FetchContent 拉取 v1.14.0)。可通过 -DNEXEC_BUILD_TESTS=OFF 跳过。
默认且受支持的测试路径是头文件模式(NEXEC_ENABLE_MODULES=OFF)。
在 GCC 16/libstdc++ 16 下,同时启用 NEXEC_ENABLE_MODULES=ON 与 GoogleTest
的 test_module_import 当前会因标准库模块与头文件声明冲突而构建失败;这不是
nexec 头文件模式的限制。需要测试 modules + GoogleTest 时请优先使用 Clang。
需要本机安装 gcovr。对 include/nexec 行覆盖率执行 100% 门禁;未启用 io_uring 时排除 uring_backend.hpp。
cmake -B build-cov -DNEXEC_BUILD_TESTS=ON -DNEXEC_ENABLE_COVERAGE=ON \
-DCMAKE_BUILD_TYPE=Debug -DNEXEC_IO_BACKEND=thread_pool
cmake --build build-cov --target coverage报告目录:build-cov/coverage/(HTML)。生成脚本使用 gcovr --merge-lines,用于合并头文件-only 模板行的计数,避免重复统计导致门槛误判。
默认仍使用头文件:#include <nexec/execution.hpp>。
若需要 import nexec;,需 CMake ≥ 3.28、Ninja 生成器(-G Ninja),并建议使用 Clang 16+(-DCMAKE_CXX_COMPILER=clang++)。开启方式:
cmake -S . -B build-modules -G Ninja \
-DNEXEC_ENABLE_MODULES=ON -DCMAKE_CXX_COMPILER=clang++
cmake --build build-modules
./build-modules/example_module_basic
ctest --test-dir build-modules -R test_module_import --output-on-failure要求:Clang 16+(首版以 Clang 验证;不保证 MSVC)。GCC 14+ 可用于常规 头文件构建,但 GCC 16/libstdc++ 16 的 modules + GoogleTest 组合存在上文所述 冲突,当前不属于受支持的测试路径。
import nexec;
auto result = nexec::SyncWait(nexec::Just(42));同一翻译单元内请只选一种引入方式:import nexec; 或 #include <nexec/execution.hpp>,不要混用。
对外 cmake --install / vcpkg 打包以头文件 INTERFACE 库为准;modules 模式不作为安装主路径。
#include <nexec/execution.hpp>
auto sender = nexec::Just(42);
auto result = nexec::SyncWait(std::move(sender));
auto [value] = result.value(); // value == 42auto sender = nexec::Just(21)
| nexec::Then([](int x) { return x * 2; });
auto result = nexec::SyncWait(std::move(sender));
auto [value] = result.value(); // value == 42#include <nexec/execution.hpp>
nexec::Task<int> GetValueAsync() {
int val = co_await (
nexec::Just(20)
| nexec::Then([](int x) { return x * 2; })
);
co_return val + 2;
}nexec::Task<void> WorkFlow(nexec::ThreadPoolScheduler scheduler) {
co_await scheduler.Schedule();
std::cout << "当前执行线程: " << std::this_thread::get_id() << std::endl;
}SenderAwaiter<Sender>:通过实现await_ready、await_suspend和await_resume,将底层协程句柄包装为 Receiver 送入发送器,使得发送器支持co_await。- 强制复制消除技术:由于底层异步操作状态
OperationState不可移动,SenderAwaiter内部使用OpStateType*并在堆上通过new进行原位构造,通过 C++17 的 Guaranteed Copy Elision 完美避开了移动构造被删除的限制。 Task<T>:实现为一个标准的 C++20 协程承诺类(Promise Concept),在最终挂起阶段 (final_suspend) 自主判定:若作为Sender连接,则通知 downstream 接收器;若作为普通协程,则顺延恢复父协程帧,保证其双重身份。
版本变更见 CHANGELOG.md。
本项目采用 MIT License,Copyright (c) 2026 Nick Huhuqihan@live.coms。