协程的未来 co_await
Future with Coroutines co_await
观看了一次 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)
returns 如何以及何时控制 compute
。即 std::future
如何实现可等待接口(类型)。
how std::future implements an awaitable interface
就 C++20 而言,它不会。 C++20 提供 co_await
及其附带的语言功能,但不提供任何实际可等待的类型。
std::future
如何实现可等待接口与并发 TS std::experimental::future
实现future::then
的方式基本相同。当 future
的值可用时,then
接受一个函数继续执行。 then
的 return 值是一个新的 future<U>
(旧的 future<T>
现在变得无效),其中 U
是给定延续的新值函数 returns。新的 future
只有在原始值可用且延续已将其处理为新值时才具有 U
可用。按照这个顺序。
关于 .then
工作原理的确切细节完全取决于 future
的实现方式。这可能取决于特定的 future
是如何 创建的 ,因为来自 std::async
的 future
具有其他 future
的特殊属性不要。
co_await
只是让这个过程在视觉上更容易理解。 co_await
able future 只需将协程句柄推入 future::then
,从而改变 future
.
这里有一个完整的程序,可以使用 C++20 协程等待未来。这几天自己做了学习。
#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)
观看了一次 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)
returns 如何以及何时控制 compute
。即 std::future
如何实现可等待接口(类型)。
how std::future implements an awaitable interface
就 C++20 而言,它不会。 C++20 提供 co_await
及其附带的语言功能,但不提供任何实际可等待的类型。
std::future
如何实现可等待接口与并发 TS std::experimental::future
实现future::then
的方式基本相同。当 future
的值可用时,then
接受一个函数继续执行。 then
的 return 值是一个新的 future<U>
(旧的 future<T>
现在变得无效),其中 U
是给定延续的新值函数 returns。新的 future
只有在原始值可用且延续已将其处理为新值时才具有 U
可用。按照这个顺序。
关于 .then
工作原理的确切细节完全取决于 future
的实现方式。这可能取决于特定的 future
是如何 创建的 ,因为来自 std::async
的 future
具有其他 future
的特殊属性不要。
co_await
只是让这个过程在视觉上更容易理解。 co_await
able future 只需将协程句柄推入 future::then
,从而改变 future
.
这里有一个完整的程序,可以使用 C++20 协程等待未来。这几天自己做了学习。
#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)