使用协程时boost::asio超时处理的最佳实践探讨
更简洁的Boost.Asio协程超时实现方案
你这种手动启动定时器协程、手动管理socket和定时器交互的方式确实有点繁琐,Boost.Asio在协程模式下提供了更优雅的超时实现思路——让连接操作和定时器操作竞争完成,谁先结束就优先处理谁,不需要手动维护复杂的协程间交互逻辑。
优化后的实现代码
我们可以用Boost.Asio的experimental::awaitable_operators(Boost 1.78+版本支持,稳定性已经足够生产环境使用)来实现“先完成即返回”的逻辑,代码会紧凑很多:
#define BOOST_ASIO_HAS_CO_AWAIT #define BOOST_ASIO_HAS_STD_COROUTINE #include <iostream> #include <chrono> #include <thread> #include <array> #include <boost/asio/awaitable.hpp> #include <boost/asio/co_spawn.hpp> #include <boost/asio/connect.hpp> #include <boost/asio/io_context.hpp> #include <boost/asio/ip/address_v4.hpp> #include <boost/asio/ip/tcp.hpp> #include <boost/asio/executor_work_guard.hpp> #include <boost/asio/experimental/awaitable_operators.hpp> namespace this_coro = boost::asio::this_coro; using namespace std::chrono_literals; // 启用awaitable运算符支持,让我们可以用||实现竞争等待 using namespace boost::asio::experimental::awaitable_operators; boost::asio::awaitable<void> fail_to_connect() { auto executor = co_await this_coro::executor; boost::asio::ip::tcp::socket socket{executor}; const auto ep = boost::asio::ip::tcp::endpoint{ boost::asio::ip::make_address_v4("192.168.1.52"), 80}; boost::asio::steady_timer timer{executor, 5s}; std::cout << "Thread ID: " << std::this_thread::get_id() << " Starting connection" << std::endl; // 同时等待连接操作和定时器,谁先完成就先返回结果 auto [connect_result, timer_result] = co_await ( boost::asio::async_connect(socket, std::array{ep}, boost::asio::use_awaitable) || timer.async_wait(boost::asio::use_awaitable) ); if (timer_result) { // 定时器先触发,说明连接超时 std::cout << "Thread ID: " << std::this_thread::get_id() << " Connection timed out after 5s" << std::endl; socket.close(); // 关闭socket终止未完成的连接操作 } else if (connect_result) { // 连接成功(你的场景里不会走到这里,但保留逻辑完整性) std::cout << "Thread ID: " << std::this_thread::get_id() << " Connected successfully" << std::endl; timer.cancel(); // 取消定时器避免无用触发 // 这里可以添加后续的连接处理逻辑 socket.close(); } } int main() { auto ctx = boost::asio::io_context{}; auto guard = boost::asio::make_work_guard(ctx.get_executor()); // 协程异常处理,捕获并输出错误信息 boost::asio::co_spawn(ctx, fail_to_connect, [](std::exception_ptr e) { if (e) { try { std::rethrow_exception(e); } catch (const boost::system::system_error& err) { // 可以在这里判断是否是预期的操作取消异常,避免不必要的输出 if (err.code() != boost::asio::error::operation_aborted) { std::cout << "Exception: " << err.what() << std::endl; } } } }); ctx.run(); }
方案优势
- 代码更简洁直观:不需要手动启动单独的定时器协程,用
||运算符就能直接表达“连接或定时器谁先完成就处理谁”的逻辑,符合协程的异步编程思维。 - 自动处理交互逻辑:不需要手动维护socket关闭和定时器取消的时机——超时触发时关闭socket会自动终止连接操作;连接成功时取消定时器避免后续无用的触发。
- 线程上下文安全:所有协程操作都在同一个执行器上运行,和你原来的实现一样,不会出现socket的并发访问问题。
运行输出示例
对应你的场景,运行后会得到类似这样的输出:
Thread ID: 140171855615808 Starting connection Thread ID: 140171855615808 Connection timed out after 5s
(注:我们在异常处理里过滤了operation_aborted错误,这是socket关闭导致连接操作终止的预期异常,不需要额外输出)
内容的提问来源于stack exchange,提问作者cmannett85
相关产品推荐
相关产品推荐

