C++ Coroutine简单学习教程
协程是一种用户态线程,可以避免线程切换等开销,提高代码效率。C++20引入了支持协程的新关键字co_await、co_yield、co_return等,可以方便地实现协程。本教程将介绍C++协程的基本使用方法。
环境准备
在编译器中开启协程支持,需要使用/await
选项。
使用clang++编译示例代码:
clang++ -std=c++20 -fcoroutines-ts -stdlib=libc++ -lc++abi main.cpp
基本语法
协程相关关键字:
co_await
:等待另一个协程的完成,并将结果返回给调用者co_yield
:将控制权暂时交回给调用者,并将一个值返回给调用者co_return
:协程结束,并返回结果
协程可以分为两种:生成器和异步函数。
生成器是一种协程,它可以generator_yield返回一个值,而后再次resume时从generator_yield挂起的地方继续执行。
异步函数是一个返回期望对象的函数,期望对象将在异步操作完成时获取值。
使用示例
示例一:生成器
#include <iostream>
#include <experimental/coroutine>
class Generator {
public:
struct promise_type;
using CoroutineHandle = std::experimental::coroutine_handle<promise_type>;
struct promise_type {
int current_value;
auto get_return_object() {
return Generator{CoroutineHandle::from_promise(*this)};
}
auto initial_suspend() {
return std::experimental::suspend_always{};
}
auto final_suspend() {
return std::experimental::suspend_always{};
}
void unhandled_exception(){
std::terminate();
}
auto yield_value(int value){
current_value = value;
return std::experimental::suspend_always{};
}
};
Generator(CoroutineHandle handle) : coro{handle} {}
~Generator() {
if(coro) {
coro.destroy();
}
}
int get() {
coro.resume();
return coro.promise().current_value;
}
bool moveNext()
{
if (coro.done())
{
return false; // 生成器已经结束
}
coro.resume(); // 继续执行生成器
return !coro.done();
}
private:
CoroutineHandle coro;
};
Generator gen() {
int i = 0;
while (true) {
co_yield i++;
}
}
int main() {
auto g = gen();
for (int i = 0; i < 10; ++i) {
std::cout << g.get() << "\n";
g.moveNext();
}
}
输出:
0
1
2
3
4
5
6
7
8
9
这里就写了一个简单的生成器,每次调用 get() 函数就可以获得生成器的下一个值。生成器在每次挂起的时候,调用者将会得到一个 yield_value 的返回值,并且当生成器结束时,将会在 final_suspend 函数里面清理资源。
示例二:异步函数
#include <iostream>
#include <experimental/coroutine>
#include <chrono>
#include <thread>
std::experimental::coroutine_handle<> async_handle;
struct Async {
struct promise_type {
std::experimental::suspend_never get_return_object() {
return {};
}
std::experimental::suspend_always initial_suspend() {
return {};
}
std::experimental::suspend_always final_suspend() noexcept {
return {};
}
void return_void() {}
};
};
void async_test() {
async_handle.resume();
}
Async async_sleep(int ms) {
std::thread t([ms](){
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
async_test();
});
t.detach();
co_await async_handle;
}
int main() {
async_handle = std::experimental::coroutine_handle<>::from_address(nullptr);
async_sleep(1000); //等待1000ms
std::cout << "async_sleep complete\n";
}
上述代码中,我们使用了一个异步函数来实现等待一段时间的效果。当我们在 async_sleep 中调用 co_await async_handle 的时候,我们就将当前协程挂起,直到 async_test 函数中再次调用了 async_handle.resume() ,协程才会继续执行。
输出:
async_sleep complete
这种方式相较于线程等待,协程的切换成本更低,可以更加高效地实现异步。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++ Coroutine简单学习教程 - Python技术站