协程 就是一个可挂起可复原执行的函数.
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