C++ 协程的 Promise Type:自定义协程行为的接口

好的,系好安全带,各位观众老爷们!今天咱们要聊的是C++协程里一个相当重要的角色——Promise Type。这玩意儿,说白了,就是协程的“管家婆”,负责协程的创建、启动、暂停、恢复、以及最终的返回值和异常处理。没有它,协程就像没了缰绳的野马,指不定跑到哪儿去了。

Promise Type 是个啥?

你可以把 Promise Type 看作是一个类,这个类定义了协程的行为。编译器会根据你定义的 Promise Type,生成协程的“骨架”。这个骨架包括:

  • 协程帧 (Coroutine Frame): 协程的所有局部变量、参数、以及状态信息都存储在这里。
  • 状态机 (State Machine): 控制协程在不同执行点之间的切换。
  • Promise 对象 (Promise Object): 咱们今天要重点讲的,负责协程的生命周期管理。

编译器会默默地创建 Promise Type 的实例,并将其与协程帧关联起来。这意味着 Promise 对象可以访问协程的所有局部变量和参数,并且可以控制协程的执行流程。

为什么需要 Promise Type?

C++协程的设计哲学是“零开销抽象”。也就是说,协程本身不应该引入额外的运行时开销。Promise Type 的存在,允许你自定义协程的行为,而无需修改编译器或者运行时库。

通过自定义 Promise Type,你可以实现以下功能:

  • 自定义协程的启动方式: 例如,可以异步启动协程,或者延迟启动协程。
  • 自定义协程的返回值: 例如,可以返回一个 future 对象,或者一个自定义的异步结果对象。
  • 自定义协程的异常处理: 例如,可以将异常转换为错误码,或者记录异常信息。
  • 实现生成器 (Generator): 让协程可以产生一系列的值,而不是仅仅返回一个值。
  • 实现异步任务队列: 将协程作为异步任务提交到队列中,等待执行。

总而言之,Promise Type 给了你极大的灵活性,让你可以根据自己的需求定制协程的行为。

Promise Type 的关键方法

一个 Promise Type 通常需要实现以下几个关键方法:

方法名 作用 返回值类型
promise_type() 构造函数,用于创建 Promise 对象。 N/A
~promise_type() 析构函数,用于销毁 Promise 对象。 N/A
get_return_object() 返回协程的返回值对象。这个对象通常是一个 future 对象,或者一个自定义的异步结果对象。 通常是 future 或自定义的异步结果类型
initial_suspend() 协程启动时执行的第一个方法。通常返回一个 std::suspend_never 或者 std::suspend_always 对象,用于控制协程是否立即挂起。 std::suspend_neverstd::suspend_always
final_suspend() 协程结束时执行的最后一个方法。通常返回一个 std::suspend_never 或者 std::suspend_always 对象,用于控制协程是否立即销毁。 std::suspend_neverstd::suspend_always
return_void() 协程没有返回值时调用的方法。 void
return_value(T value) 协程返回一个值时调用的方法。T 是返回值的类型。 void
unhandled_exception() 协程抛出异常时调用的方法。可以在这个方法中处理异常,例如记录异常信息,或者将异常转换为错误码。 void
yield_value(T value) 用于生成器,协程产生一个值时调用的方法。T 是产生的值的类型。这个方法通常会将值存储在一个队列中,供调用者读取。 std::suspend_always

一个简单的 Promise Type 示例

让我们来看一个简单的 Promise Type 示例,它返回一个 std::future<int> 对象。

#include <future>
#include <iostream>
#include <coroutine>

struct MyPromise {
    int result;
    std::promise<int> promise;

    MyPromise() {
        std::cout << "MyPromise::MyPromise()" << std::endl;
    }

    ~MyPromise() {
        std::cout << "MyPromise::~MyPromise()" << std::endl;
    }

    std::future<int> get_return_object() {
        std::cout << "MyPromise::get_return_object()" << std::endl;
        return promise.get_future();
    }

    std::suspend_never initial_suspend() noexcept {
        std::cout << "MyPromise::initial_suspend()" << std::endl;
        return {}; // 立即开始执行协程
    }

    std::suspend_never final_suspend() noexcept {
        std::cout << "MyPromise::final_suspend()" << std::endl;
        return {}; // 协程结束后立即销毁
    }

    void return_value(int value) {
        std::cout << "MyPromise::return_value(" << value << ")" << std::endl;
        result = value;
        promise.set_value(value); // 设置 future 的值
    }

    void unhandled_exception() {
        std::cout << "MyPromise::unhandled_exception()" << std::endl;
        promise.set_exception(std::current_exception()); // 设置 future 的异常
    }

    struct promise_type {
        MyPromise get_return_object() { return MyPromise{}; }
        std::suspend_never initial_suspend() noexcept { return {}; }
        std::suspend_never final_suspend() noexcept { return {}; }
        void return_value(int value) { get_return_object().return_value(value); }
        void unhandled_exception() { get_return_object().unhandled_exception(); }

    };
};

MyPromise::promise_type operator co_await(std::future<int>& f) {
    return {};
}

MyPromise::promise_type operator co_await(std::future<void>& f) {
    return {};
}

// 协程函数
MyPromise::promise_type my_coroutine() {
    std::cout << "my_coroutine() - started" << std::endl;
    co_return 42; // 返回值
}

int main() {
    std::future<int> future = my_coroutine().get_return_object();
    std::cout << "Waiting for result..." << std::endl;
    int result = future.get(); // 等待协程完成并获取结果
    std::cout << "Result: " << result << std::endl;
    return 0;
}

在这个例子中:

  • MyPromise 类定义了协程的行为。
  • get_return_object() 方法返回一个 std::future<int> 对象,用于获取协程的返回值。
  • return_value() 方法设置 future 的值。
  • unhandled_exception() 方法设置 future 的异常。
  • initial_suspend()final_suspend() 方法都返回 std::suspend_never,表示协程立即开始执行,并在结束后立即销毁。
  • my_coroutine 函数使用 co_return 返回一个值。

生成器 (Generator) 的 Promise Type

生成器是一种特殊的协程,它可以产生一系列的值,而不是仅仅返回一个值。要实现一个生成器,我们需要使用 yield_value() 方法。

#include <iostream>
#include <coroutine>
#include <vector>

template <typename T>
struct Generator {
    struct promise_type {
        T current_value;
        std::coroutine_handle<promise_type> coroutine;

        Generator get_return_object() {
            return Generator{std::coroutine_handle<promise_type>::from_promise(*this)};
        }

        std::suspend_always initial_suspend() noexcept { return {}; }
        std::suspend_always final_suspend() noexcept { return {}; }
        void unhandled_exception() { std::terminate(); }

        std::suspend_always yield_value(T value) {
            current_value = value;
            return {};
        }
        void return_void() {}
    };

    std::coroutine_handle<promise_type> handle;

    Generator(std::coroutine_handle<promise_type> handle) : handle(handle) {}

    ~Generator() {
        if (handle) handle.destroy();
    }

    // 禁用拷贝构造和赋值
    Generator(const Generator&) = delete;
    Generator& operator=(const Generator&) = delete;

    bool move_next() {
        handle.resume();
        return !handle.done();
    }

    T current_value() {
        return handle.promise().current_value;
    }
};

Generator<int> my_generator(int limit) {
    for (int i = 0; i < limit; ++i) {
        co_yield i;
    }
}

int main() {
    auto generator = my_generator(5);
    while (generator.move_next()) {
        std::cout << generator.current_value() << std::endl;
    }
    return 0;
}

在这个例子中:

  • Generator 模板类定义了生成器的行为。
  • promise_type 内部类定义了 Promise Type。
  • yield_value() 方法将产生的值存储在 current_value 成员变量中,并返回 std::suspend_always,表示协程挂起。
  • move_next() 方法恢复协程的执行,并返回一个布尔值,表示是否还有更多值可以产生。
  • current_value() 方法返回当前产生的值.
  • my_generator() 函数使用 co_yield 产生一系列的值。

异步任务队列的 Promise Type

我们可以使用 Promise Type 来实现一个简单的异步任务队列。

#include <iostream>
#include <coroutine>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <thread>
#include <future>

class TaskQueue {
public:
    struct task {
        std::coroutine_handle<> handle;
    };

    void enqueue(std::coroutine_handle<> handle) {
        {
            std::lock_guard<std::mutex> lock(mutex_);
            queue_.push({handle});
        }
        cv_.notify_one();
    }

    void run() {
        while (true) {
            std::unique_lock<std::mutex> lock(mutex_);
            cv_.wait(lock, [this] { return !queue_.empty(); });

            task t = queue_.front();
            queue_.pop();
            lock.unlock();

            if (t.handle) {
                t.handle.resume();
                if (t.handle.done()) {
                    t.handle.destroy();
                }
            }
        }
    }

private:
    std::queue<task> queue_;
    std::mutex mutex_;
    std::condition_variable cv_;
};

TaskQueue task_queue;

struct Task {
    struct promise_type {
        Task get_return_object() { return {std::coroutine_handle<promise_type>::from_promise(*this)}; }
        std::suspend_always initial_suspend() { return {}; }
        std::suspend_always final_suspend() noexcept { return {}; }
        void return_void() {}
        void unhandled_exception() { std::terminate(); }
        void return_value(int value) {} // 可以添加返回值处理
    };

    std::coroutine_handle<promise_type> handle;

    Task(std::coroutine_handle<promise_type> handle) : handle(handle) {}

    ~Task() {
       // std::cout << "Task Destroyed" << std::endl; // 避免竞争
    }

    // 禁用拷贝构造和赋值
    Task(const Task&) = delete;
    Task& operator=(const Task&) = delete;
};

Task my_async_task(int id) {
    std::cout << "Task " << id << " started on thread: " << std::this_thread::get_id() << std::endl;
    co_await std::suspend_never{}; // 模拟一些工作
    std::cout << "Task " << id << " finished on thread: " << std::this_thread::get_id() << std::endl;
}

int main() {
    std::thread worker_thread([&]() { task_queue.run(); }); // 启动工作线程

    for (int i = 0; i < 5; ++i) {
        auto task = my_async_task(i);
        task_queue.enqueue(task.handle);
    }

    std::this_thread::sleep_for(std::chrono::seconds(2)); // 允许任务完成
    std::cout << "Main thread exiting." << std::endl;
    // 为了简单起见,这里不优雅地停止工作线程。 正确的做法是添加一个停止信号。
    return 0;
}

在这个例子中:

  • TaskQueue 类维护一个任务队列,并使用一个工作线程来执行队列中的任务。
  • Task 结构体定义了 Promise Type。
  • initial_suspend() 方法返回 std::suspend_always,表示协程立即挂起,等待被添加到任务队列中。
  • enqueue() 方法将协程句柄添加到任务队列中。
  • run() 方法从任务队列中取出协程句柄,并恢复协程的执行。
  • my_async_task() 函数是一个异步任务,它使用 co_await 挂起自身,等待被任务队列执行。

总结

Promise Type 是 C++ 协程中一个非常重要的概念。它允许你自定义协程的行为,例如自定义返回值、异常处理、以及实现生成器和异步任务队列。掌握 Promise Type 的使用,可以让你更好地利用 C++ 协程的强大功能。

友情提示:

  • Promise Type 的设计需要谨慎,错误的实现可能会导致程序崩溃或者内存泄漏。
  • 在实现 Promise Type 时,要充分考虑线程安全问题,避免出现竞争条件。
  • 多阅读一些开源协程库的源码,可以帮助你更好地理解 Promise Type 的使用。

好了,今天的讲座就到这里。希望各位观众老爷们有所收获,早日成为协程大师! 记住,代码要多敲,bug要多调,才能真正掌握这门技术。 拜拜了您嘞!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注