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

如何基于C++20协程实现通用的谓词等待类?

实现C++20协程通用谓词等待逻辑的方案

核心思路

要把「循环等待谓词+挂起」的逻辑抽象成通用组件,需要实现一个自定义awaitable类型,配合Executor的扩展,将等待中的协程从常规任务队列分离,在每次Executor调度时检查谓词状态,满足条件后再将协程重新加入执行队列。

具体实现步骤

1. 扩展Executor,添加等待协程管理

修改Executor,新增存储等待协程及对应谓词的列表,在每次调度前检查这些等待项:

#include <functional>
#include <deque>
#include <coroutine>

// 保留原有Task和task_promise_t定义
struct task_promise_t {
  struct Task : std::coroutine_handle<task_promise_t> {
    using promise_type = task_promise_t;
  };

  Task get_return_object() { return {Task::from_promise(*this)}; }
  std::suspend_always initial_suspend() noexcept { return {}; }
  std::suspend_always final_suspend() noexcept { return {}; }

  void return_void() {}
  void unhandled_exception() {}
};

using Task = task_promise_t::Task;

struct Executor {
  void enqueue_task(Task &&task) { tasks.push_back(std::move(task)); }

  void tick() {
    // 先处理等待中的协程,检查谓词
    process_waiting_tasks();
    while (!tasks.empty())
      step();
  }

  void step() {
    // 每次step前先检查等待队列
    process_waiting_tasks();
    
    if (tasks.empty()) return;
    
    Task task = std::move(tasks.front());
    tasks.pop_front();
    task.resume();
    if (!task.done())
      tasks.push_back(std::move(task));
  }

  // 新增:注册等待谓词的协程
  void add_waiting_task(Task task, std::function<bool()> predicate) {
    waiting_tasks.emplace_back(std::move(task), std::move(predicate));
  }

private:
  std::deque<Task> tasks;
  // 存储等待的协程和对应的谓词
  std::deque<std::pair<Task, std::function<bool()>>> waiting_tasks;

  void process_waiting_tasks() {
    auto it = waiting_tasks.begin();
    while (it != waiting_tasks.end()) {
      if (it->second()) {
        // 谓词满足,移回执行队列
        enqueue_task(std::move(it->first));
        it = waiting_tasks.erase(it);
      } else {
        ++it;
      }
    }
  }
};

2. 实现通用的WaitFor awaitable

定义WaitFor类,持有Executor指针和等待谓词,实现awaitable的三个必要方法:

struct WaitFor {
  Executor* exec;
  std::function<bool()> predicate;

  explicit WaitFor(Executor* e, std::function<bool()> pred)
    : exec(e), predicate(std::move(pred)) {}

  // 先检查谓词是否已经满足,避免不必要的挂起
  bool await_ready() const noexcept {
    return predicate();
  }

  // 挂起时,将协程加入Executor的等待列表
  void await_suspend(std::coroutine_handle<> handle) const noexcept {
    // 将通用handle转换为Task类型
    auto task = Task::from_promise(*static_cast<task_promise_t*>(handle.promise()));
    exec->add_waiting_task(std::move(task), predicate);
  }

  // 恢复时无额外操作
  void await_resume() const noexcept {}
};

3. 验证使用示例

按照预期方式使用,即可实现通用的谓词等待:

#include <iostream>

int main() {
  Executor exec;
  static bool global_predicate = false;

  exec.enqueue_task([](Executor* exec) -> Task {
    co_await WaitFor(exec, []() -> bool { return global_predicate; });
    std::cout << "Hello, world" << std::endl;
    co_return;
  }(&exec));

  exec.step(); // 无输出,谓词未满足
  exec.step(); // 无输出,谓词未满足
  global_predicate = true;
  exec.step(); // 输出"Hello, world",协程执行完成
  return 0;
}

关键细节说明

  • await_ready优化:先检查谓词状态,如果已经满足,直接跳过挂起,减少不必要的调度开销。
  • 协程类型转换:在await_suspend中,将通用的std::coroutine_handle<>转换为自定义的Task类型,确保能正确存入Executor的等待列表。
  • Executor调度时机:在step和tick开头都调用process_waiting_tasks,保证每次调度前都检查等待条件,避免错过谓词状态变化。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 22:45:39