C++—协程

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

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 
#include 
#include 
#include 
#include 

using namespace std::literals;

using Callback = std::function;

// 异步执行(模拟耗时的计算)
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;

    ~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 
#include 

struct Generator
{
    struct promise_type;
    using handle_t = std::coroutine_handle;

    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

你可能感兴趣的:(c++coroutine协程)