1 协程
协程(Coroutines)是一个可以挂起执行以便稍后恢复的函数。协程是无堆栈的:它们通过返回到调用方来暂停执行,并且恢复执行所需的数据与堆栈分开存储。这允许异步执行的顺序代码(例如,在没有显式回调的情况下处理非阻塞I/O),还支持惰性计算无限序列上的算法和其他用途。
协程类图如下:
2 实例
C++20 标准增加了协程,其头文件是coroutine
实例代码如下:
#include <coroutine> //for std::coroutine_handle std::suspend_never
#include <iostream>
#include <stdexcept>
#include <thread>
auto switch_to_new_thread(std::jthread& out)
{
struct awaitable
{
std::jthread* p_out;
bool await_ready()
{
std::cerr << "1.await_ready" << std::endl;
return false;
}
void await_suspend(std::coroutine_handle<> h)
{
std::jthread& out = *p_out;
if (out.joinable())
throw std::runtime_error("Output jthread parameter not empty");
out = std::jthread([h] { h.resume(); });
std::cerr << "2.await_suspend" << std::endl;
std::cout << "新线程ID: " << out.get_id() << '\n'; // this is OK
}
void await_resume()
{
std::cerr << "3.await_resume" << std::endl;
}
};
return awaitable{&out};
}
struct task
{
struct promise_type
{
task get_return_object() {
std::cerr << "1.get_return_object" << std::endl;
return {};
}
std::suspend_never initial_suspend()
{
std::cerr << "2.initial_suspend" << std::endl;
return {};
}
std::suspend_never final_suspend() noexcept
{
std::cerr << "4.final_suspend" << std::endl;
return {};
}
void return_void()
{
std::cerr << "3.return_void" << std::endl;
}
void unhandled_exception()
{
std::cerr << "5.unhandled_exception" << std::endl;
}
};
};
task resuming_on_new_thread(std::jthread& out)
{
std::cout << "协程在线程上启动: " << std::this_thread::get_id() << '\n';
co_await switch_to_new_thread(out);
// awaiter destroyed here
std::cout << "协程在线程上恢复: " << std::this_thread::get_id() << '\n';
}
int main()
{
std::jthread out;
resuming_on_new_thread(out);
}
说明:
- 对于awaitable,必须实现接口:
- await_ready
- await_suspend
- await_resume
- 对task,必须定义类型promise_type且promise_type必须实现接口:
- get_return_object
- initial_suspend
- final_suspend
- return_void或return_value或yield_value
- unhandled_exception
3 编译
$ g++ coroutinines.cpp -o coro
In file included from coroutinines.cpp:1:
/usr/include/c++/10/coroutine:295:2: error: #error "the coroutine header requires -fcoroutines"
295 | #error "the coroutine header requires -fcoroutines"
| ^~~~~
coroutinines.cpp:6:32: error: ‘jthread’ is not a member of ‘std’; did you mean ‘thread’?
6 | auto switch_to_new_thread(std::jthread& out)
增加编译选项-fcoroutines
$ g++ -fcoroutines coroutinines.cpp -o coro
coroutinines.cpp:6:32: error: ‘jthread’ is not a member of ‘std’; did you mean ‘thread’?
6 | auto switch_to_new_thread(std::jthread& out)
| ^~~~~~~
| thread
增加编译选项-std=c++20使用C++20标准
g++ -std=c++20 coroutinines.cpp -fcoroutines -o coro
编译通过.
4 运行
$ ./coro
1.get_return_object
2.initial_suspend
协程在线程上启动: 140662219543040
1.await_ready
2.await_suspend
新线程ID: 140662219408960
3.await_resume
协程在线程上恢复: 140662219408960
3.return_void
4.final_suspend
调用流程:
- task.get_return_object
- task.initial_suspend
- awaitable.await_ready
- awaitable.await_suspend
- awaitable.await_resume
- task.return_void
- final_suspend
5 总结
- 新线程ID: 140662219408960 和协程在线程上恢复: 140662219408960的两个线程id相同,说明co_await调用作为一个分界线,之上是一个线程,之下是一个线程