如何基于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
相关产品推荐
相关产品推荐

