关于c++:C协程

3次阅读

共计 3763 个字符,预计需要花费 10 分钟才能阅读完成。

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

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
正文完
 0