【发布时间】:2020-06-29 09:38:10
【问题描述】:
我正在基于 windows io 完成端口的异步套接字类中添加对协程 ts 的支持。 如果没有协程,io 可能会像这样完成:
sock.async_write(io::buffer(somebuff), [](auto&& ... args){ /* in handler */ });
或
sock.async_write(std::vector<io::const_buffer>{ ... }, [](auto&& ... args){ /* in handler */ })
其中每个都会返回void,并通过handler通知结果,不需要缓存参数,因为从函数返回时操作已经提交
但是对于协程,该函数将返回一个可等待对象,在使用 operator co_await 等待它时将提交操作,因此我需要将参数缓存在可等待对象中以避免使用已破坏的临时对象:
awaitable coro_write(const io::const_buffer& buff)
{
return awaitable{ *this, buff };
}
awaitable coro_write(const std::vector<io::const_buffer>& buffs)
{
return awaitable{ *this, buffs };
}
第一个副本不会造成伤害,但第二个会造成伤害,因为它会触发堆分配并复制向量内容。
所以我正在寻找解决方案,在阅读此页面 coroutines ts 时,我遇到了这个:
典型的生成器的 yield_value 会将其参数存储(复制/移动或仅存储地址,因为参数的生命周期跨越 co_await 内的暂停点)到生成器对象中并返回 std::suspend_always,将控制权转移给调用者/简历。
并且在同一页面中指出co_yield 表达式等同于:
co_await promise.yield_value(expr)
这也类似于:
co_await sock.coro_write(expr)
我打开了Visual Studio 2019附带的生成器头,看到它也将参数的地址存储到yield_value,并在协程暂停后通过调用方站点中的generator::iterator::operator *()稍后检索:
struct promise_type {
_Ty const* _CurrentValue;
auto yield_value(_Ty const& _Value) {
_CurrentValue = _STD addressof(_Value);
return suspend_always{};
}
}
struct iterator {
_NODISCARD reference operator*() const {
return *_Coro.promise()._CurrentValue;
}
_NODISCARD pointer operator->() const {
return _Coro.promise()._CurrentValue;
}
}
由此我得出结论,传递给返回与co_await 一起使用的等待器的函数的参数也将保持有效,直到协程恢复或销毁,对吗?或者这对于 Promise 类型中的 yield_value 是特殊的?
【问题讨论】:
标签: c++ coroutine c++20 c++-coroutine