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

如何使用Boost Asio实现恢复C++20协程的一次性事件类?

用Boost Asio实现C++20协程的一次性事件类(oneshot_event)

核心实现代码

针对需求,以下是一个线程安全的oneshot_event实现,支持单个或多个协程等待,调用raise()时立即恢复所有挂起的协程:

#include <boost/asio.hpp>
#include <coroutine>
#include <vector>
#include <optional>

namespace async = boost::asio;

struct oneshot_event {
    // 传入执行器,确保所有操作在同一上下文(线程安全)
    explicit oneshot_event(async::any_io_executor ex)
        : executor_(std::move(ex)) {}

    // 触发事件,线程安全
    void raise() {
        async::post(executor_, [this]() {
            if (!raised_) {
                raised_ = true;
                // 恢复所有等待的协程
                for (auto coro : waiters_) {
                    coro.resume();
                }
                waiters_.clear();
            }
        });
    }

    // 等待事件,可被co_await
    async::awaitable<void> wait() {
        struct awaiter {
            oneshot_event& event_;
            std::coroutine_handle<> coro_;

            // 检查事件是否已触发,避免不必要的挂起
            bool await_ready() const noexcept {
                return event_.raised_;
            }

            // 挂起协程,保存句柄到等待列表
            void await_suspend(std::coroutine_handle<> coro) noexcept {
                coro_ = coro;
                event_.waiters_.push_back(coro);
            }

            // 协程恢复时的操作,无返回值
            void await_resume() noexcept {}
        };

        co_await awaiter{*this};
    }

private:
    async::any_io_executor executor_;
    bool raised_ = false;
    std::vector<std::coroutine_handle<>> waiters_; // 存储等待的协程句柄
};

代码解释

  1. 线程安全保障:

    • 构造函数接收any_io_executor(比如io_context::get_executor()或strand包装的执行器),所有状态修改操作通过async::post提交到执行器上下文,避免多线程并发访问的竞态问题。
    • raise()会在执行器的线程中完成状态更新和协程恢复,确保操作原子性。
  2. Awaitable实现:

    • wait()返回的awaitable内部定义了符合C++20协程要求的awaiter结构体,实现三个核心方法:
      • await_ready():提前检查事件是否已触发,若已触发则协程直接继续,无需挂起。
      • await_suspend():将当前协程的句柄加入等待列表,完成挂起逻辑。
      • await_resume():协程恢复时无额外操作,仅作为协程继续执行的入口。
  3. 多等待者支持:

    • 通过std::vector<std::coroutine_handle<>>存储所有等待的协程句柄,调用raise()时一次性恢复所有挂起的协程,解决了旧Boost.Coroutine类无法适配多协程等待的问题。

Boost Asio与C++20协程的协作机制

  • Boost Asio的awaitable是对C++20无栈协程的封装,自动将协程调度与Asio的执行上下文(io_context/strand)绑定,确保协程在正确的线程中恢复。
  • 使用co_await挂起协程时,Asio会将协程句柄注册到事件源(这里是oneshot_event),待事件触发后再将协程重新加入执行队列完成恢复。
  • 旧的Boost.Coroutine基于栈式协程,与C++20无栈协程机制完全不同,且旧coro类仅针对单个协程生命周期控制,无法方便实现多协程等待同一事件的场景,也难以与Asio异步模型无缝集成。

使用示例

async::awaitable<void> demo(async::io_context& io_ctx) {
    oneshot_event event(io_ctx.get_executor());

    // 启动两个协程等待事件
    async::co_spawn(io_ctx, [&event]() -> async::awaitable<void> {
        co_await event.wait();
        std::cout << "Coroutine 1 resumed\n";
    }, async::detached);

    async::co_spawn(io_ctx, [&event]() -> async::awaitable<void> {
        co_await event.wait();
        std::cout << "Coroutine 2 resumed\n";
    }, async::detached);

    // 1秒后触发事件
    co_await async::steady_timer(io_ctx, std::chrono::seconds(1)).async_wait(async::use_awaitable);
    event.raise();
}

int main() {
    async::io_context io_ctx;
    async::co_spawn(io_ctx, demo(io_ctx), async::detached);
    io_ctx.run();
    return 0;
}

注意事项

  • 若需要可重置事件,可添加reset()方法,重置raised_为false并清空等待列表,但需确保操作在执行器上下文中完成,保证线程安全。
  • 禁止在未绑定执行器的情况下使用oneshot_event,否则会出现线程安全问题。
  • oneshot_event触发后,后续调用wait()会直接返回,不会挂起协程。

内容的提问来源于stack exchange,提问作者tuxifan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 13:52:03