Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Context(协程上下文)

asco::context(底层定义在 asco::contexts::context)提供了一个轻量的协程取消原语,可用于在多个协程之间传播“停止/超时”信号。它与 notify 结合使用,支持显式取消与超时自动取消。

  • 头文件:#include <asco/context.h>
  • 命名空间:asco(通过别名导出)

创建方式

  • static std::shared_ptr<context> with_cancel()
    • 创建一个手动可取消的上下文,初始状态为 未取消
  • static std::shared_ptr<context> with_timeout(const duration_type auto &dur)
    • 创建一个上下文并在 dur 后自动取消。内部会启动一个协程调用 cancel()

两个工厂函数都返回 std::shared_ptr<context>。使用共享指针便于在多个协程中传播同一取消源。

取消与状态查询

  • future<void> cancel()
    • 将上下文标记为已取消,并唤醒所有等待该上下文的协程,然后执行取消回调。该协程可以被并发调用;每次调用都会执行已注册的回调。
  • future<void> set_cancel_callback(std::function<void()>)
    • 注册一个在取消发生时调用的回调。若上下文已取消,请先重新检查状态;回调仅在随后执行的 cancel() 协程中触发。
    • 回调实现必须是可重入的,并且要做好幂等处理,以便在并发取消时安全地再次触发取消或操作上下文。
  • bool is_cancelled() const noexcept
    • 查询当前是否处于已取消状态。

取消操作是幂等的;重复调用 cancel() 不会带来额外副作用。

等待取消

context 定义了成员 operator co_await(),同时也为 std::shared_ptr<context> 提供了自由函数 operator co_await()。因此既可以在上下文对象本身上 co_await ctx_ref;,也可以直接 co_await ctx_ptr; 来等待取消事件:

  • 若上下文尚未取消,当前协程将挂起,直到 cancel() 或超时触发。
  • 若上下文已取消,则立即恢复,且 co_await 不返回任何额外数据:它仅表示“取消已经发生”。

示例:

#include <asco/context.h>
#include <asco/future.h>
#include <asco/time/sleep.h>
using namespace asco;

future_spawn<void> worker(context &ctx_ref, std::atomic<bool> &flag) {
    co_await ctx_ref;            // 等待取消信号
    flag.store(true, std::memory_order_release);
    co_return;
}

future<int> async_main() {
    auto ctx = context::with_cancel();
    std::atomic<bool> flag{false};

    auto w = worker(*ctx, flag);  // 也可以 co_await ctx(shared_ptr)

    // 进行其他操作…
    co_await sleep_for(10ms);
    co_await ctx->cancel();      // 通知所有等待方并等待回调执行

    co_await w;
    return flag.load(std::memory_order_acquire) ? 0 : 1;
}

与超时结合

with_timeout() 会在后台调用 sleep_for() 后自动取消:

future<int> async_main() {
    auto ctx = context::with_timeout(50ms);

    co_await ctx;  // 最多等待 50ms

    // 此时 ctx 已经处于取消状态
    if (!ctx->is_cancelled()) {
        co_return 1;
    }
    co_return 0;
}

注意事项

  • context 仅负责取消信号的传播,不携带附加信息。若需要携带错误码或取消原因,请在业务代码中自行维护。
  • 上下文内部使用 notify 唤醒等待者;在没有协程等待时调用 cancel() 也会正确记录状态,随后等待者会立即返回。
  • 取消回调必须是可重入的,且需要自行保证并发调用时的幂等性。