【问题标题】:Future with Coroutines co_await协程的未来 co_await
【发布时间】:2020-07-28 12:20:21
【问题描述】:

观看 c++ 讲座 (https://youtu.be/DLLt4anKXKU?t=1589),我试图了解未来如何使用 co_await;示例:

auto compute = []() -> std::future<int> {
int fst = co_await std::async(get_first);
int snd = co_await std::async(get_second);
co_return fst + snd;
};
auto f = compute();
/* some heavy task */ 
f.get();

我不明白co_await std::async(get_first) 如何以及何时将控制权交还给compute。即std::future 如何实现一个等待接口(类型)。

【问题讨论】:

    标签: c++ future coroutine c++20


    【解决方案1】:

    std::future 如何实现等待接口

    就 C++20 而言,它不是。 C++20 提供了co_await 及其附带的语言功能,但它不提供任何实际的可等待类型。

    std::future可以如何实现 awaitable 接口与并发 TS 中的std::experimental::future 如何实现future::then 基本相同。当future 的值可用时,then 将继续执行函数。 then 的返回值是一个新的future&lt;U&gt;(旧的future&lt;T&gt; 现在不再起作用),其中U 是给定延续函数返回的新值。新的future 将仅在原始值可用并且延续已将其处理为新值时才有可用的U。按这个顺序。

    .then 如何工作的确切细节完全取决于future 的实现方式。这可能取决于特定的future 是如何创建的,因为来自std::asyncfutures 具有其他futures 没有的特殊属性。

    co_await 只是让这个过程在视觉上更容易理解。 co_awaitable 未来将简单地将协程句柄推入 future::then,从而改变 future

    【讨论】:

      【解决方案2】:

      这里有一个完整的程序,可以使用 C++20 协程等待 future。这些天我自己做的是为了学习。

      #include <cassert>
      #include <coroutine>
      #include <future>
      #include <iostream>
      #include <optional>
      #include <thread>
      
      using namespace std::literals;
      
      
      
      template <class T>
      class FutureAwaitable {
      public:
        template <class U> struct BasicPromiseType {
          auto get_return_object() {
            return FutureAwaitable<T>(CoroHandle::from_promise(*this));
          }
      
          std::suspend_always initial_suspend() noexcept {
            std::cout << "Initial suspend\n";
            return {};
          }
      
          std::suspend_never final_suspend() noexcept {
            std::cout << "Final suspend\n";
            return {};
          }
      
          template <class V>
          requires std::is_convertible_v<V, T>
          void return_value(V v) { _value = v; }
      
          void unhandled_exception() { throw; }
      
          std::optional<T> _value;
        };
      
        using promise_type = BasicPromiseType<FutureAwaitable<T>>;
        using CoroHandle = std::coroutine_handle<promise_type>;
      
        explicit FutureAwaitable(CoroHandle h) : _parent(h) {  }
      
        ~FutureAwaitable() {
        }
      
      
        bool is_ready() const {
            auto & fut = std::get<FutureAwaitable<T> *>(&_parent);
            return fut->wait_for(std::chrono::seconds(0)) != std::future_status::ready;
        }
      
        FutureAwaitable(std::future<T> && f) {
            _f = &f;
        }
      
        T get() const { return promise()._value.value(); }
      
        std::future<T> & std_future() const {
          assert(_f->valid());
          return *_f;
        }
      
        bool await_ready() {
          if (!(_f->wait_for(std::chrono::seconds(0)) == std::future_status::ready)) {
            std::cout << "Await ready IS ready\n";
            return true;
          }
          else
            std::cout << "Await ready NOT ready\n";
          return false;
        }
      
        auto await_resume() {
          std::cout << "Await resume" << std::endl;
          return std_future().get();
        }
      
        bool await_suspend(CoroHandle parent) {
            _parent = parent;
            std::cout << "Await suspend\n";
            return true;
        }
      
        void resume() {
          assert(_parent);
          _parent.resume();
        }
      
        auto parent() const { return _parent; }
      
        bool done() const noexcept {
          return _parent.done();
        }
      
      private:
        auto & promise() const noexcept { return _parent.promise(); }
      
        CoroHandle _parent = nullptr;
        std::future<T> * _f = nullptr;
      
      };
      
      template <class T> auto operator co_await(std::future<T> &&f) {
        return FutureAwaitable<T>(std::forward<std::future<T>>(f));
      }
      
      template <class T> auto operator co_await(std::future<T> & f) {
        return FutureAwaitable<T>(std::forward<std::future<T>>(f));
      }
      
      
      FutureAwaitable<int> coroutine() {
        std::promise<int> p;
        auto fut = p.get_future();
        p.set_value(31);
        std::cout << "Entered func()" << std::endl;
        auto res = co_await std::move(fut);
        std::cout << "Continue func(): " << res << std::endl;
      
        auto computation = co_await std::async(std::launch::async, [] {
            int j = 0;
            for (int i = 0; i < 1000; ++i) {
                j += i;
            }
            return j;
        });
      
        auto computation2 = std::async(std::launch::async, [] {
            int j = 0;
            std::this_thread::sleep_for(20s);
            for (int i = 0; i < 1000; ++i) {
                j += i;
            }
            return j;
        });
      
        auto computation3 = std::async(std::launch::async, [] {
            int j = 0;
            std::this_thread::sleep_for(20s);
            for (int i = 0; i < 1000; ++i) {
                j += i;
            }
            return j;
        });
        co_await computation2;
        co_await computation3;
      
        std::cout << "Computation result is " << computation << std::endl;
        co_return computation;
      }
      
      #define ASYNC_MAIN(coro)                        \
          int main() {                                \
          FutureAwaitable<int> c = coro();                     \
          do { c.resume(); } while (!c.done());                \
          std::cout << "The coroutine returned " << c.get(); \
          return 0; \
      }
      
      
      ASYNC_MAIN(coroutine)
      

      【讨论】:

      • 我不明白这是怎么工作的,因为你永远不会恢复暂停的 CoroHandle,也永远不会订阅未来的完成,这应该在 await_suspend(h) 方法中完成。
      猜你喜欢
      • 2021-04-23
      • 2020-04-13
      • 2021-11-15
      • 2020-07-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-26
      • 2019-07-31
      相关资源
      最近更新 更多