好的,系好安全带,各位观众老爷们!今天咱们要聊的是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_never 或 std::suspend_always |
final_suspend() |
协程结束时执行的最后一个方法。通常返回一个 std::suspend_never 或者 std::suspend_always 对象,用于控制协程是否立即销毁。 |
std::suspend_never 或 std::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要多调,才能真正掌握这门技术。 拜拜了您嘞!