Boost.Asio协程同时使用concurrent_channel与steady_timer时挂起问题
Boost.Asio协程结合steady_timer与concurrent_channel时部分协程卡在async_wait的问题
问题根源
你遇到的问题核心是**experimental::concurrent_channel的close操作与子协程async_receive的竞态,加上协程调度的不确定性**,在高并发场景下导致部分子协程无法及时触发timer重置,进而让父协程卡在co_await timer->async_wait。
具体细节:
- 父协程创建channel后立即调用
close(),随后挂起等待timer; - 子协程通过
co_spawn异步启动,其async_receive操作可能因io_context任务队列积压,在channel关闭后很久才被调度执行; - 极端情况下,experimental版本的concurrent_channel可能存在高并发下的唤醒遗漏,导致子协程的
async_receive无法完成,进而无法执行timer重置,父协程永久挂起。
你尝试的timer线程安全包装无效,因为问题根源不在timer的线程安全,而在于channel与协程调度的交互。
解决方案
方案1:替换experimental::concurrent_channel为更可靠的机制
如果业务逻辑只是需要触发子协程执行timer重置,完全可以去掉channel,改用asio::post直接触发子协程逻辑:
asio::co_spawn( ioc, [&, timer]() -> asio::awaitable<void> { asio::co_spawn( ioc, [timer]() -> asio::awaitable<void> { timer->expires_after(std::chrono::seconds{}); co_return; }, asio::detached); boost::system::error_code ec; co_await timer->async_wait(asio::redirect_error(asio::use_awaitable, ec)); ++done; countRunning.count_down(); co_return; }, asio::detached);
方案2:确保子协程的receive操作在channel关闭前已提交
如果必须使用channel,可以添加同步机制,让父协程等待子协程开始接收后再关闭channel:
asio::co_spawn( ioc, [&, timer]() -> asio::awaitable<void> { asio::experimental::concurrent_channel<void(boost::system::error_code, double)> c(ioc, 1); boost::latch receiveStarted(1); asio::co_spawn( ioc, [&, timer]() -> asio::awaitable<void> { boost::system::error_code ec; receiveStarted.count_down(); // 通知父协程已开始接收 co_await c.async_receive(asio::redirect_error(asio::use_awaitable, ec)); timer->expires_after(std::chrono::seconds{}); co_return; }, asio::detached); receiveStarted.wait(); // 等待子协程开始接收 boost::system::error_code ec; c.close(); co_await timer->async_wait(asio::redirect_error(asio::use_awaitable, ec)); ++done; countRunning.count_down(); co_return; }, asio::detached);
方案3:等待子协程完成后再等待timer
让父协程先等待子协程执行完毕,确保timer已被重置后再进入等待:
asio::co_spawn( ioc, [&, timer]() -> asio::awaitable<void> { asio::experimental::concurrent_channel<void(boost::system::error_code, double)> c(ioc, 1); // 用use_awaitable获取子协程的awaitable,而非detached auto child_task = asio::co_spawn( ioc, [&, timer]() -> asio::awaitable<void> { boost::system::error_code ec; co_await c.async_receive(asio::redirect_error(asio::use_awaitable, ec)); timer->expires_after(std::chrono::seconds{}); co_return; }, asio::use_awaitable); boost::system::error_code ec; c.close(); co_await child_task; // 等待子协程完成timer重置 co_await timer->async_wait(asio::redirect_error(asio::use_awaitable, ec)); ++done; countRunning.count_down(); co_return; }, asio::detached);
内容的提问来源于stack exchange,提问作者Pqqwetiqe
相关产品推荐
相关产品推荐

