**C++20协程:实现异步任务调度器**

在 C++20 中,协程(coroutines)被正式纳入语言标准,提供了一套低成本的非阻塞编程模型。下面将演示如何利用协程实现一个简易的异步任务调度器,并通过示例说明其使用方式。


1. 关键概念回顾

  • co_await:挂起当前协程,等待 awaiter 完成后恢复执行。
  • co_yield:向调用方产生一个值,协程被挂起,下一次 co_await 会继续执行。
  • co_return:结束协程,返回最终结果。

调度器的核心是一个事件循环,负责存储待执行的协程,并在适当时机恢复它们。


2. 基础组件

2.1 Awaitable 结构体

#include <coroutine>
#include <iostream>
#include <queue>
#include <chrono>
#include <thread>
#include <optional>

struct SleepAwaiter {
    std::chrono::milliseconds duration;
    SleepAwaiter(std::chrono::milliseconds d) : duration(d) {}

    bool await_ready() const noexcept { return false; }

    void await_suspend(std::coroutine_handle<> h) const noexcept {
        std::thread([h, this]() {
            std::this_thread::sleep_for(duration);
            h.resume();          // 计时结束后恢复协程
        }).detach();
    }

    void await_resume() const noexcept {}
};

2.2 Task 句柄

struct Task {
    struct promise_type {
        Task get_return_object() { return {}; }
        std::suspend_never initial_suspend() { return {}; }
        std::suspend_never final_suspend() noexcept { return {}; }
        void unhandled_exception() { std::terminate(); }
        void return_void() {}
    };
};

3. 调度器实现

class Scheduler {
    std::queue<std::coroutine_handle<>> tasks;

public:
    void add(std::coroutine_handle<> h) {
        tasks.push(h);
    }

    void run() {
        while (!tasks.empty()) {
            auto h = tasks.front();
            tasks.pop();
            if (!h.done()) h.resume();
        }
    }
};

Scheduler g_scheduler;   // 全局调度器

4. 协程工作示例

Task async_print(const std::string& msg, std::chrono::milliseconds delay) {
    co_await SleepAwaiter(delay);        // 等待指定时间
    std::cout << msg << std::endl;      // 输出信息
}

4.1 启动协程

int main() {
    // 将协程句柄注册到调度器
    g_scheduler.add(async_print("Hello, coroutine!", std::chrono::milliseconds(500)).get_return_object().handle);
    g_scheduler.add(async_print("Goodbye!", std::chrono::milliseconds(1000)).get_return_object().handle);

    g_scheduler.run();  // 事件循环开始
    std::this_thread::sleep_for(std::chrono::seconds(2));  // 防止主线程提前退出
    return 0;
}

运行结果:

Hello, coroutine!
Goodbye!

5. 进一步扩展

  • 优先级队列:用 std::priority_queue 替换 std::queue,为协程设置优先级。
  • 超时机制:在 await_suspend 里使用计时器检测超时,若超时则直接恢复协程并抛出异常。
  • 线程池:把 std::thread 换成固定大小的线程池,提高资源利用率。

6. 小结

C++20 的协程让异步编程变得更加直观和轻量。上述例子演示了如何构建最小可行的调度器并与协程配合使用。通过进一步改造,你可以轻松搭建出支持 IO、多线程、优先级调度等高级功能的异步框架。

发表评论