协程就是一个可挂起可复原执行的函数.

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