You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Boost.Asio中awaitable operator||定时器到期未返回问题咨询

问题描述

在Boost.Asio 1.81.0环境下,以下代码的run()函数期望通过operator||同时等待async_initiate返回的awaitable和2秒超时定时器,按预期定时器到期后协程应继续执行,但实际场景中(示例故意未调用async_initiate的handler,对应网络包未到达的情况),协程始终卡在co_await处。已知operator||会等待第一个成功完成的awaitable,定时器本应无错误完成,此现象不符合预期。

#include <boost/asio/any_completion_handler.hpp>
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/experimental/awaitable_operators.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/udp.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/use_awaitable.hpp>
#include <iostream>

namespace asio = boost::asio;
using namespace asio::experimental::awaitable_operators;

// 仅用于保持io_context处于忙碌状态
asio::awaitable<void> busy() {
    auto exec = co_await asio::this_coro::executor;

    using asio::ip::udp;
    auto socket = udp::socket(exec, udp::endpoint(udp::v4(), 40000));

    uint8_t msg[1024];
    std::cout << "reading from socket...\n";
    co_await socket.async_receive(asio::buffer(msg), asio::use_awaitable);
}

std::optional<asio::any_completion_handler<void(int)>> stored;

asio::awaitable<void> run() {
    std::cout << "run() called\n";

    auto exec = co_await asio::this_coro::executor;
    asio::steady_timer timer{exec, std::chrono::seconds(2)};

    auto initiate = [&]([[maybe_unused]] asio::any_completion_handler<void(int)> handler) {
        // 故意不调用handler
        // 将handler存入stored是为了排除handler析构带来的副作用
        stored = std::move(handler);
    };

    co_await (asio::async_initiate<const asio::use_awaitable_t<>, void(int)>(initiate, asio::use_awaitable)
              || timer.async_wait(asio::use_awaitable));
    std::cout << "done\n";
}

int main() {
    asio::io_context io;

    asio::co_spawn(io, busy, asio::detached);
    asio::co_spawn(io, run, asio::detached);
    io.run();
}
问题分析与解决

核心原因

问题源于async_initiate的模板参数与传入的use_awaitable类型不匹配:

  • 代码中指定async_initiate的第一个模板参数为const asio::use_awaitable_t<>,但实际传入的use_awaitable是非const的asio::use_awaitable_t<>实例,这种类型不匹配导致Boost.Asio内部生成的awaitable处于异常状态。
  • 当定时器到期时,operator||尝试取消另一个awaitable,但因该awaitable状态异常,取消逻辑无法生效,最终协程被卡在co_await处无法继续执行。

修复方案

只需移除async_initiate模板参数中的const限定,使其与传入的use_awaitable类型一致:

co_await (asio::async_initiate<asio::use_awaitable_t<>, void(int)>(initiate, asio::use_awaitable)
          || timer.async_wait(asio::use_awaitable));

或者更简洁地省略模板参数,让Boost.Asio自动推导正确类型:

co_await (asio::async_initiate(initiate, asio::use_awaitable)
          || timer.async_wait(asio::use_awaitable));

验证效果

修改后,定时器到期时operator||会正确识别第一个完成的awaitable(定时器),触发协程继续执行并输出done,即使async_initiate的handler未被调用,协程也不会再卡住。

内容的提问来源于stack exchange,提问作者dvd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 09:59:54