好的,没问题!我们现在开始进入协程句柄的世界!
各位观众,晚上好!今天我们要聊聊C++协程里的一个关键角色——std::coroutine_handle
,也就是协程句柄。这玩意儿,初听起来感觉很高大上,但其实理解起来并不难,就像…就像剥洋葱,一层一层地扒开,总能看到核心的美味(虽然有些人觉得剥洋葱会辣眼睛)。
什么是协程句柄?
简单来说,std::coroutine_handle
是一个指向协程实例的指针。它允许你在协程外部控制协程的生命周期,比如恢复执行、销毁协程等等。你可以把它想象成一个遥控器,用来控制你的协程机器人。
为什么我们需要协程句柄?
因为协程不像普通函数那样,调用完就彻底结束了。协程可以挂起,可以恢复,可以在不同的时间点执行不同的代码。如果没有一个句柄来追踪和控制它,那简直就是一场灾难。想象一下,你的协程像脱缰的野马一样到处乱跑,你却无能为力,这感觉酸爽吗?
std::coroutine_handle
的类型
std::coroutine_handle
本身是一个模板类,可以接受一个模板参数,用来指定协程返回类型。
std::coroutine_handle<>
:这是一个通用的协程句柄,可以指向任何类型的协程。std::coroutine_handle<PromiseType>
:这是一个指定了Promise类型的协程句柄。Promise类型是协程返回类型中的一个成员,它定义了协程的行为。
创建 std::coroutine_handle
创建协程句柄通常发生在协程返回对象的 promise().get_return_object()
函数中。这个函数负责返回一个包含协程句柄的对象,这个对象通常是 std::coroutine_token
或 std::coroutine_future
或其他自定义类型。
让我们看一个简单的例子:
#include <iostream>
#include <coroutine>
struct MyCoroutine {
struct promise_type {
int value;
MyCoroutine get_return_object() {
return {std::coroutine_handle<promise_type>::from_promise(*this)};
}
std::suspend_never initial_suspend() { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
void unhandled_exception() { std::terminate(); }
void return_void() {}
};
std::coroutine_handle<promise_type> handle;
MyCoroutine(std::coroutine_handle<promise_type> h) : handle(h) {}
~MyCoroutine() {
if (handle) handle.destroy();
}
};
MyCoroutine my_coroutine() {
std::cout << "Coroutine startedn";
co_await std::suspend_always{};
std::cout << "Coroutine resumedn";
}
int main() {
MyCoroutine coro = my_coroutine();
coro.handle.resume();
std::cout << "Main function resumedn";
return 0;
}
在这个例子中:
- 我们定义了一个
MyCoroutine
结构体,它包含一个std::coroutine_handle<promise_type>
类型的成员handle
。 MyCoroutine::promise_type
定义了协程的 Promise 类型,其中get_return_object()
函数返回一个MyCoroutine
对象,该对象包含了协程的句柄。my_coroutine()
函数是一个协程,它返回MyCoroutine
对象。- 在
main()
函数中,我们调用my_coroutine()
创建一个协程,并使用coro.handle.resume()
恢复协程的执行。
std::coroutine_handle
的常用操作
std::coroutine_handle
提供了一些重要的操作,让我们来逐一介绍:
-
resume()
:恢复协程执行这是最常用的操作之一。
resume()
函数会恢复协程的执行,让它从上次挂起的地方继续运行。就像按下了机器人的启动按钮,让它开始工作。std::coroutine_handle<> handle = ...; // 获取协程句柄 handle.resume(); // 恢复协程执行
-
destroy()
:销毁协程destroy()
函数会销毁协程实例,释放它所占用的内存。在使用完协程后,一定要记得调用destroy()
,否则会造成内存泄漏。这就像用完的机器人,要记得关机并回收,不然会浪费电。std::coroutine_handle<> handle = ...; // 获取协程句柄 handle.destroy(); // 销毁协程
-
done()
:检查协程是否完成done()
函数会返回一个布尔值,指示协程是否已经完成。如果协程已经执行到final_suspend()
点或者抛出了未处理的异常,done()
就会返回true
。std::coroutine_handle<> handle = ...; // 获取协程句柄 if (handle.done()) { std::cout << "Coroutine is donen"; } else { std::cout << "Coroutine is still runningn"; }
-
address()
:获取协程实例的地址address()
函数返回协程实例的内存地址。这个地址可以用来进行一些底层的操作,比如调试。std::coroutine_handle<> handle = ...; // 获取协程句柄 void* addr = handle.address(); // 获取协程实例的地址
-
*`from_address(void addr)`:从地址创建协程句柄**
这是一个静态成员函数,可以从一个内存地址创建一个协程句柄。这个函数通常用于一些特殊的场景,比如从外部存储恢复协程的状态。
void* addr = ...; // 协程实例的地址 std::coroutine_handle<> handle = std::coroutine_handle<>::from_address(addr); // 从地址创建协程句柄
-
from_promise(PromiseType& promise)
:从 Promise 对象创建协程句柄这是一个静态成员函数,可以从一个 Promise 对象创建一个协程句柄。这个函数通常在协程返回对象的
promise().get_return_object()
函数中使用。struct MyCoroutine { struct promise_type { MyCoroutine get_return_object() { return {std::coroutine_handle<promise_type>::from_promise(*this)}; } ... }; ... };
一个更完整的例子
让我们来看一个更完整的例子,它演示了如何使用 std::coroutine_handle
来控制协程的生命周期:
#include <iostream>
#include <coroutine>
struct Task {
struct promise_type {
int result;
Task get_return_object() {
return Task{std::coroutine_handle<promise_type>::from_promise(*this)};
}
std::suspend_never initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() { std::terminate(); }
void return_value(int value) { result = value; }
};
std::coroutine_handle<promise_type> handle;
Task(std::coroutine_handle<promise_type> h) : handle(h) {}
~Task() {
if (handle) handle.destroy();
}
int get_result() {
return handle.promise().result;
}
};
Task my_task() {
std::cout << "Task startedn";
co_await std::suspend_always{};
std::cout << "Task resumedn";
co_return 42;
}
int main() {
Task task = my_task();
std::cout << "Main function resumedn";
task.handle.resume();
std::cout << "Task result: " << task.get_result() << std::endl;
return 0;
}
在这个例子中:
- 我们定义了一个
Task
结构体,它包含一个std::coroutine_handle<promise_type>
类型的成员handle
。 Task::promise_type
定义了协程的 Promise 类型,其中get_return_object()
函数返回一个Task
对象,该对象包含了协程的句柄。return_value()
函数用于设置协程的返回值。my_task()
函数是一个协程,它返回Task
对象。- 在
main()
函数中,我们调用my_task()
创建一个协程,并使用task.handle.resume()
恢复协程的执行。最后,我们使用task.get_result()
获取协程的返回值。
使用 std::coroutine_handle
的注意事项
- 生命周期管理: 你需要小心管理协程的生命周期。确保在使用完协程后调用
destroy()
,避免内存泄漏。 - 线程安全:
std::coroutine_handle
本身不是线程安全的。如果你需要在多个线程中使用同一个协程句柄,你需要自己进行同步。 - 异常处理: 协程中抛出的未处理异常会导致程序终止。你需要在
promise_type
中提供unhandled_exception()
函数来处理异常。
*std::coroutine_handle
与 `void` 的转换**
std::coroutine_handle
可以与 void*
进行转换,这在某些场景下非常有用。例如,你可以将协程句柄存储在外部存储中,然后在需要的时候从存储中恢复协程句柄。
// 将 std::coroutine_handle 转换为 void*
void* ptr = handle.address();
// 将 void* 转换为 std::coroutine_handle
std::coroutine_handle<> recovered_handle = std::coroutine_handle<>::from_address(ptr);
表格总结
操作 | 描述 |
---|---|
resume() |
恢复协程的执行。 |
destroy() |
销毁协程实例,释放内存。 |
done() |
检查协程是否完成。 |
address() |
获取协程实例的地址。 |
from_address() |
从地址创建协程句柄。 |
from_promise() |
从 Promise 对象创建协程句柄。 |
高级用法:自定义 Promise 类型
std::coroutine_handle
的威力在于它可以与自定义的 Promise 类型一起使用。通过自定义 Promise 类型,你可以完全控制协程的行为。
例如,你可以自定义一个 Promise 类型,实现协程的取消功能:
#include <iostream>
#include <coroutine>
#include <atomic>
struct CancellableTask {
struct promise_type {
int result;
std::atomic<bool> cancelled{false};
CancellableTask get_return_object() {
return {std::coroutine_handle<promise_type>::from_promise(*this)};
}
std::suspend_never initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() { std::terminate(); }
void return_value(int value) { result = value; }
auto await_transform(std::suspend_always) {
struct Awaiter {
promise_type* promise;
bool await_ready() { return false; }
void await_suspend(std::coroutine_handle<> h) {
if (promise->cancelled) {
h.destroy(); // 立即销毁协程
}
}
void await_resume() {}
};
return Awaiter{this};
}
};
std::coroutine_handle<promise_type> handle;
CancellableTask(std::coroutine_handle<promise_type> h) : handle(h) {}
~CancellableTask() {
if (handle) handle.destroy();
}
int get_result() {
return handle.promise().result;
}
void cancel() {
handle.promise().cancelled = true;
handle.resume(); // 尝试恢复协程,让 await_transform 执行
}
};
CancellableTask my_cancellable_task() {
std::cout << "Task startedn";
co_await std::suspend_always{};
std::cout << "Task resumedn";
co_return 42;
}
int main() {
CancellableTask task = my_cancellable_task();
std::cout << "Main function resumedn";
task.cancel(); // 取消协程
std::cout << "Task cancelledn";
return 0;
}
在这个例子中,我们在 Promise 类型中添加了一个 cancelled
标志,并在 await_transform
中检查这个标志。如果 cancelled
为 true
,我们就立即销毁协程。
总结
std::coroutine_handle
是 C++ 协程中一个非常重要的组成部分。它允许你控制协程的生命周期,恢复协程的执行,并获取协程的状态。通过自定义 Promise 类型,你可以实现更高级的协程功能,比如取消、超时等等。
希望今天的讲座能帮助你更好地理解 std::coroutine_handle
。记住,协程的世界很广阔,还有很多值得探索的地方。继续学习,继续实践,你一定会成为协程大师!
感谢大家的观看,下课!