如何用C++20协程结合Boost.Asio等待多个CompletionToken?
背景
我正在使用Boost.Asio,调用多个异步函数后希望等待多个CompletionToken,例如async_read()和steady_timer::async_wait()。
我已通过Boost.Asio的无栈协程实现该逻辑,运行正常。
场景中有两个定时器,第一个定时器时长比第二个长,因此第二个会先触发并取消第一个。第一个定时器模拟async_read(),第二个作为其超时器。
无栈协程实现方案
#include <iostream> #include <chrono> #include <boost/asio.hpp> namespace as = boost::asio; #include <boost/asio/yield.hpp> template <typename Executor> struct my_app { my_app(Executor exe) :exe_{std::move(exe)}, worker_{*this} {} private: friend struct worker; struct worker { worker(my_app& ma) :ma_{ma} { // initiate coroutine (*this)(boost::system::error_code{}, std::string{}); } void operator()( boost::system::error_code const& ec, std::string timer_str // to distinguish timer ) const { reenter(coro_) { std::cout << "start" << std::endl; yield { // set two timers // timer1 is a kind of receive emulation // timer2 is a receive timeout ma_.t1_.expires_after(std::chrono::milliseconds(200)); ma_.t1_.async_wait( as::append( *this, "timer1" ) ); ma_.t2_.expires_after(std::chrono::milliseconds(100)); ma_.t2_.async_wait( as::append( *this, "timer2" ) ); } if (ec) { std::cout << timer_str << " " << ec.message() << std::endl; } else { std::cout << timer_str << " fired" << std::endl; if (timer_str == "timer2") { ma_.t1_.cancel(); } } } } my_app& ma_; mutable as::coroutine coro_; }; Executor exe_; as::steady_timer t1_{exe_}; as::steady_timer t2_{exe_}; worker worker_; }; #include <boost/asio/unyield.hpp> int main() { as::io_context ioc; my_app ma{ioc.get_executor()}; ioc.run(); }
如何用C++20协程实现相同逻辑?
我尝试从无栈协程迁移到C++20协程,但找不到等待多个CompletionToken的方法,该如何实现?
已尝试方案
分步使用co_await
#include <iostream> #include <chrono> #include <boost/asio.hpp> namespace as = boost::asio; template <typename Executor> as::awaitable<void> proc(Executor exe) { as::steady_timer t1{exe, std::chrono::milliseconds{200}}; as::steady_timer t2{exe, std::chrono::milliseconds{100}}; // how to multiwait t1 and t2 ? co_await t1.async_wait(as::use_awaitable); std::cout << "timer fired1" << std::endl; co_await t2.async_wait(as::use_awaitable); std::cout << "timer fired2" << std::endl; } int main() { as::io_context ioc; as::co_spawn(ioc, proc(ioc.get_executor()), as::detached); ioc.run(); }
该方案无法正常工作,因为第二个定时器是在第一个触发后才设置的。
使用多个co_spawn
#include <iostream> #include <chrono> #include <boost/asio.hpp> namespace as = boost::asio; as::awaitable<void> proc1(as::steady_timer& t1) { try { co_await t1.async_wait(as::use_awaitable); std::cout << "timer1 fired" << std::endl; } catch (std::runtime_error const& ec) { std::cout << "timer1 " << ec.what() << std::endl; } } as::awaitable<void> proc2(as::steady_timer& t1, as::steady_timer& t2) { try { co_await t2.async_wait(as::use_awaitable); std::cout << "timer2 fired" << std::endl; t1.cancel(); } catch (std::runtime_error const& ec) { std::cout << "timer2 " << ec.what() << std::endl; } } int main() { as::io_context ioc; as::steady_timer t1{ioc.get_executor(), std::chrono::seconds{2}}; as::steady_timer t2{ioc.get_executor(), std::chrono::seconds{1}}; as::co_spawn(ioc, proc1(t1), as::detached); as::co_spawn(ioc, proc2(t1, t2), as::detached); ioc.run(); }
该方案技术上可行,但存在问题,例如似乎无法在返回值为as::awaitable<T>的可等待函数中工作,我认为有更优方案。
环境
- Boost 1.83.0
- x86-64 clang 17.0.1
内容的提问来源于stack exchange,提问作者Takatoshi Kondo
相关产品推荐
相关产品推荐

