跳转到内容

内置运行时(v2)

dart_cpp_bridge 内置一个由 Asio 和 stdexec 组成的进程级 Runtime。 DartCppBridge.init() 会自动启动它,C++ 业务代码无需再创建一套事件循环。

  • Asio io_context — 默认一个 runner,负责 bridge dispatch 和非阻塞任务; runner 数量可在启动前配置;
  • IoContextScheduler — v2 的 stdexec scheduler,由 Runtime::io_scheduler() 返回;
  • Asio thread pool — blocking scheduler,由 Runtime::blocking_scheduler() 返回;
  • 通道co::oneshotco::mpsc sender,用于跨线程通信。

Runtime 头文件通过 DCB_ASIO_NS 暴露当前选择的 Asio 实现:默认展开为 asio,设置 DCB_USE_BOOST_ASIO=ON 后展开为 boost::asio。业务代码应 使用 DCB_ASIO_NS::...,这样同一份源码可以适配两种实现。宿主工程提供 stdexec/Asio 时的 CMake 配置见依赖所有权与 Asio 命名空间

#include <dart_cpp_bridge/runtime.hpp>
#include <stdexec/execution.hpp>
auto& runtime = dcb::Runtime::instance();
runtime.start(); // 通常由 Dart init 完成
auto* io = runtime.io_scheduler(); // 默认一个 runner
auto blocking = runtime.blocking_scheduler();

stdexec::task<T> 编写通过 BRIDGE_ASYNC 暴露的协程:

#include <dart_cpp_bridge/annotate.h>
#include <dart_cpp_bridge/runtime.hpp>
#include <stdexec/execution.hpp>
#include <string>
BRIDGE_ASYNC
stdexec::task<std::string> delayed_echo(std::string message) {
co_await dcb::sleep(std::chrono::milliseconds(100));
co_return message;
}

生成的 dispatch 会在 bridge scheduler 上启动 task。协程挂起时不会占用 io 线程。使用 starts_oncontinues_ononwhen_allsync_wait 等当前 stdexec 名称,不要在 v2 中使用 v1 的 Lazy / Executor API。

不要阻塞 io 线程。需要保持在异步流水线中时,用 dcb::spawn_blocking

BRIDGE_ASYNC
stdexec::task<int> compute(int n) {
auto value = co_await dcb::spawn_blocking([n] {
return expensive_synchronous_work(n);
});
co_return value;
}

完全普通的阻塞函数使用 BRIDGE_NORMAL;生成的 dispatch 会把它放到 blocking pool 执行,但 Dart 侧仍得到 Future<T>

dcb::sync_wait(sender) 是给非协程调用方使用的阻塞便利函数。它会拒绝在 任何 io scheduler runner 上调用,因为在那里等待会造成事件循环自死锁。即使 配置了多个 runner,原始 stdexec::sync_wait 也只是在还有空闲 runner 时可能 完成;它会占用调用线程,所有 runner 同时等待时仍会让 scheduler 死锁:

auto result = dcb::sync_wait(
stdexec::starts_on(*dcb::Runtime::instance().io_scheduler(),
stdexec::just(42)));

请从 worker 或外部线程调用,不要从 BRIDGE_SYNC 业务代码调用。

auto [tx, rx] = dcb::co::oneshot::channel<std::string>();
tx.send("hello");
// 在 stdexec::task 中:
auto value = co_await std::move(rx); // std::optional<std::string>

对于 mpsc channel,使用 co_await rx.recv();oneshot receiver 本身就是 sender。recv() 会观察 stop token。v2 使用 stdexec::inplace_stop_source / stdexec::stop_token 做协作式取消,取消后 通过 set_stopped() 完成。Dart Future 不能被强制取消,需要时应由业务层 额外暴露 task ID 和 cancel 方法。

io scheduler 默认有 1 个 runner。必须在第一个 session 启动 Runtime 前通过 DartCppBridge.init(ioThreads: 2) 配置;C++ 可以在 start() 前调用 Runtime::set_io_threads(2)。传入 0 会规范化为 1,启动后修改会被忽略。

内置 blocking pool 默认有 4 个线程。必须在第一个 session 启动 Runtime 前 通过 DartCppBridge.init(poolThreads: 8)(或生成 API 的 threadPoolSize) 配置。C++ 可以在 start() 前调用 Runtime::set_pool_threads(8)。如果某类 工作需要独立线程池,把它的 scheduler 作为 dcb::spawn_blocking 的第二个 参数传入,详见线程与阻塞任务