【C++学习(36)】C++20的co_await 的不同使用方式和特性
这三个代码片段的目的是展示 C++ 中 协程(Coroutines)的不同使用方式和特性。每个代码展示了不同类型的协程调度和挂起/恢复机制。
1. 第一个代码:使用 std::experimental::coroutine_handle
手动控制协程恢复
关键点:
- 手动控制协程的恢复:
myCoroutine.resume()
手动恢复协程执行。 - 使用
coroutine_handle
保存协程:myCoroutine
是一个全局变量,存储了当前协程的句柄,以便后续在myTask()
中恢复协程。
解析:
该示例中展示了一个手动控制协程的例子:
myTask
中的myCoroutine.resume()
表示手动恢复协程,调用它启动协程的执行。asyncFunc
中使用co_await MyAwaitable{}
来挂起当前协程,并且通过await_suspend
将协程句柄保存(即myCoroutine = handle
),以便后续恢复。- 这种方式展示了如何手动控制协程的挂起与恢复。
主要学习内容:
- 如何通过
std::experimental::coroutine_handle
控制协程的恢复。 co_await
和await_suspend
的基本用法。
#include <iostream>
#include <experimental/coroutine>
std::experimental::coroutine_handle<> myCoroutine;
void myTask() {
std::cout << "Starting coroutine..." << std::endl;
myCoroutine.resume(); // 启动协程
std::cout << "Resuming execution..." << std::endl;
}
struct MyAwaitable {
bool await_ready() const { return false; }
void await_suspend(std::experimental::coroutine_handle<> handle) const {
myCoroutine = handle; // 将当前协程保存起来
}
void await_resume() const {}
};
MyAwaitable asyncFunc() {
std::cout << "Suspending execution..." << std::endl;
co_await MyAwaitable{}; // 挂起协程,等待恢复
std::cout << "Resumed execution..." << std::endl;
}
int main() {
myTask();
asyncFunc().await_resume();
return 0;
}
2. 第二个代码:使用 std::experimental::suspend_always
自动恢复协程
关键点:
- 协程自动恢复:使用
handle.resume()
在await_suspend
中自动恢复协程。 - 使用
suspend_always
:std::experimental::suspend_always
是一种简单的挂起机制,协程会在执行到co_await
时自动挂起,直到resume()
被调用。
解析:
在此示例中:
- 协程
asyncFunc
在执行到co_await MyAwaitable{}
时会被挂起。 MyAwaitable
结构体的await_suspend
函数通过调用handle.resume()
自动恢复协程的执行。await_ready
返回false
,表示协程总是会被挂起。
主要学习内容:
- 如何使用
suspend_always
来让协程在执行时自动挂起,直到外部调用resume()
恢复。 - 使用
handle.resume()
恢复协程执行的基本用法。
#include <iostream>
#include <experimental/coroutine>
struct MyAwaitable {
bool await_ready() const { return false; }
void await_suspend(std::experimental::coroutine_handle<> handle) const {
std::cout << "Suspending coroutine..." << std::endl;
handle.resume(); // 恢复协程的执行
}
void await_resume() const {}
};
std::experimental::suspend_always asyncFunc() {
std::cout << "Starting coroutine..." << std::endl;
co_await MyAwaitable{}; // 使用 co_await 挂起协程
std::cout << "Resuming execution..." << std::endl;
}
int main() {
auto coro = asyncFunc();
coro.resume(); // 启动协程
return 0;
}
3. 第三个代码:协程与异步操作结合(例如异步请求)
关键点:
- 与异步操作结合:结合了 C++ 的
std::future
和协程,通过await_suspend
在异步操作完成后恢复协程。 - 模拟异步请求:通过
std::promise
和std::future
模拟了一个异步请求,协程在等待结果时被挂起,异步请求完成后恢复协程。
解析:
该代码片段展示了如何使用协程与异步操作(如线程和 std::future
)配合:
makeAsyncRequest
函数创建了一个异步操作(通过std::thread
模拟延迟的任务),返回一个Awaitable
对象。- 在
Awaitable
中,await_ready
判断异步结果是否已经准备好,如果没有准备好,await_suspend
会将当前协程挂起,直到异步操作完成。 - 当
std::future
中的结果准备好时,协程会被恢复执行。
主要学习内容:
- 协程与异步操作(如
std::future
)的结合使用。 - 使用
std::promise
和std::future
模拟异步操作,并结合协程来实现异步任务的等待与恢复。
#include <iostream>
#include <future>
#include <experimental/coroutine>
struct Awaitable {
std::future<int> fut;
bool await_ready() {
return fut.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
}
void await_suspend(std::experimental::coroutine_handle<> handle) {
fut.then([handle](std::future<int> f) mutable {
// 异步操作完成后恢复协程执行
handle.resume();
});
}
int await_resume() {
return fut.get();
}
};
Awaitable makeAsyncRequest() {
std::promise<int> p;
auto fut = p.get_future();
// 模拟异步操作
std::thread([&p]() mutable {
std::this_thread::sleep_for(std::chrono::seconds(2));
p.set_value(42);
}).detach();
return Awaitable{std::move(fut)};
}
std::experimental::suspend_always task() {
auto result = co_await makeAsyncRequest(); // 等待异步操作完成,并获取结果
std::cout << "Result: " << result << std::endl;
}
int main() {
auto coro = task();
coro.resume(); // 启动协程
return 0;
}
总结:
- 第一个代码展示了手动控制协程的恢复和执行,重点在于如何使用
std::experimental::coroutine_handle
来手动恢复协程。 - 第二个代码展示了如何使用
std::experimental::suspend_always
来让协程自动挂起,并通过handle.resume()
来恢复协程。 - 第三个代码展示了如何将协程与异步操作(如
std::future
)结合使用,模拟了异步请求并在请求完成后恢复协程的执行。
这些例子帮助理解 C++20 协程的核心机制,尤其是如何通过 co_await
、await_suspend
和 std::future
等方式进行协程挂起与恢复。