协程就是一个可挂起可复原执行的函数.
C++ 中的协程是无栈式的:当协程被挂起时,它将返回到调用者,且复原执行所须要的相干数据并不保留在栈上.
协程的益处是:
- 容许以串行的形式编写异步执行的代码,而无需显式应用回调;
- 能够实现惰性计算(如,生成器).
1. 协程
一个函数只有蕴含如下之一便是协程:
应用
co_await
运算符来挂起执行;size_t n = co_await socket.async_read_some(buffer(data));
应用
co_yield
关键字来挂起执行,并返回一个值;co_yield n++;
应用
co_return
关键字来完结执行,并可选地返回一个值.co_return;co_return 7;
2. co_await
co_await
运算符的操作数必须是 Awaitable. 一个对象如果有如下3个成员函数,则它就是 Awaitable:
await_ready
:协程开始时会调用此函数,如果返回true
,示意你想得到的后果曾经失去了,协程不须要执行了. 所以大部分状况这个函数的实现是要返回false
.await_suspend
:执行co_await Awaitable
时便会执行此函数,它会挂起协程. 该函数会传入一个coroutine_handle
类型的参数,这是一个由编译器生成的变量. 在此函数中调用handle.resume()
就能够复原协程.await_resume
:复原协程运行时便会调用此函数. 这个函数的返回值就是co_await
运算符的返回值.
3. promise_type
设协程的返回值类型为 Task
,则 Task
必须蕴含一个外部类 promise_type
,且 promise_type
须要蕴含如下成员函数:
struct promise_type { /* 结构协程的返回值 */ Task get_return_object() { return Task{}; } /* 在执行协程体之前调用 */ std::suspend_never initial_suspend() { return std::suspend_never{}; } /* 在执行完协程体之后调用 */ std::suspend_never final_suspend() noexcept { return std::suspend_never{}; } /* 执行 co_return; 时调用 只需定义 return_void 或 return_value 其中之一便可 */ void return_void() {} /* 执行 co_return value; 时调用 */ void return_value(T value) { // ... } /* 执行 co_yield value; 时执行 如果不须要用到 co_yield,则无需定义 */ auto yield_value(T value) { // ... return std::suspend_always{}; } /* 异样解决 */ void unhandled_exception() {}};
4. 例子1:应用协程来解决异步逻辑
#include <iostream>#include <chrono>#include <coroutine>#include <thread>#include <functional>using namespace std::literals;using Callback = std::function<void(int)>;// 异步执行(模仿耗时的计算)void asyncCompute(int v, Callback cb){ std::thread t([v, cb]() { std::this_thread::sleep_for(10ms); int result = v + 100; cb(result); }); t.detach();}// 协程的返回值类型struct Task{ struct promise_type; using handle_t = std::coroutine_handle<promise_type>; ~Task() { if (m_handle) { m_handle.destroy(); } } Task(const Task&) = delete; Task& operator=(const Task&) = delete; Task(Task&& task) noexcept : m_handle(std::move(task.m_handle)) { task.m_handle = handle_t::from_address(nullptr); } Task& operator=(Task&& task) noexcept { m_handle = std::move(task.m_handle); task.m_handle = handle_t::from_address(nullptr); }private: Task(handle_t h): m_handle(h) {}public: struct promise_type { Task get_return_object() { return Task(handle_t::from_promise(*this)); } std::suspend_never initial_suspend() { return std::suspend_never{}; } std::suspend_never final_suspend() noexcept { return std::suspend_never{}; } void return_void() {} void unhandled_exception() { std::cout << "unhandled exception\n"; } };private: handle_t m_handle;};// co_await 操作数的类型class ComputeAwaitable{public: ComputeAwaitable(int initValue) : m_init(initValue), m_result(0) {} bool await_ready() { return false; } // 调用异步函数 void await_suspend(std::coroutine_handle<> handle) { auto cb = [handle, this](int value) mutable { m_result = value; handle.resume(); }; asyncCompute(m_init, cb); } int await_resume() { return m_result; }private: int m_init; int m_result;};Task computeByCoroutine(int v){ int ret = co_await ComputeAwaitable(v); ret = co_await ComputeAwaitable(ret); ret = co_await ComputeAwaitable(ret); std::cout << ret << '\n'; co_return;}int main(){ Task task(computeByCoroutine(200)); std::this_thread::sleep_for(3s);}
500
5. 例子2:生成器
#include <iostream>#include <coroutine>struct Generator{ struct promise_type; using handle_t = std::coroutine_handle<promise_type>; struct promise_type { int value; auto get_return_object() { return Generator(handle_t::from_promise(*this)); } auto initial_suspend() { return std::suspend_always(); } auto final_suspend() noexcept { return std::suspend_always(); } void return_void() {} auto yield_value(int v) { value = v; return std::suspend_always(); } void unhandled_exception() { std::terminate(); } }; bool next() { if (m_handle) { m_handle.resume(); return !m_handle.done(); } return false; } int value() const { return m_handle.promise().value; } ~Generator() { if (m_handle) { m_handle.destroy(); } }private: Generator(handle_t h) : m_handle(h) {}private: handle_t m_handle;};Generator f(int n){ int value = 1; while(value <= n) { co_yield value++; }}int main(){ Generator g(f(10)); while (g.next()) { std::cout << g.value() << ' '; }}
1 2 3 4 5 6 7 8 9 10