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

C++20环境下co_await组合async_read与async_wait触发未捕获异常问题

C++20中co_await组合async_read与async_wait引发未捕获异常修复方案

问题现象

  • 程序触发无法捕获的异常
  • 出现boost::asio::detail::cancellation_handler_base::call[virtual] == 0xFFFFFFFFFFFFFFFF错误,对应错误代码片段:
void emit(cancellation_type_t type)
{
  if (handler_)
    handler_->call(type);
}

问题根源

使用||操作符组合async_read和async_wait时,其中一个操作完成后Asio会自动取消另一个未完成的操作。原代码存在两个关键问题:

  1. 服务端循环中复用同一个定时器对象,重复调用expires_after导致旧的等待操作被取消时,内部handler管理出现非法状态,触发未定义行为。
  2. 异步操作的错误处理不统一,部分操作使用use_awaitable抛出异常,部分使用use_nothrow_awaitable返回错误,导致取消逻辑中的异常无法被正确捕获。

修复方案

  1. 循环内创建新定时器对象:避免复用定时器导致的旧操作取消时的handler问题。
  2. 统一使用use_nothrow_awaitable:所有异步操作都通过返回值处理错误,避免抛出未捕获异常。
  3. 完善错误处理逻辑:操作失败时及时退出循环,防止无效的后续操作。

修复后的服务端代码

#include <iostream>
#include <boost/asio.hpp>
#include <boost/asio/thread_pool.hpp>
#include <boost/asio/experimental/awaitable_operators.hpp>
#include <boost/asio/experimental/as_tuple.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/signal_set.hpp>
#include <cstdio>

using boost::asio::ip::tcp;
using boost::asio::awaitable;
using boost::asio::co_spawn;
using boost::asio::detached;
using boost::asio::steady_timer;
using boost::asio::use_awaitable;
using boost::asio::thread_pool;
using std::chrono::steady_clock;
using boost::asio::buffer;
namespace this_coro = boost::asio::this_coro;

constexpr auto use_nothrow_awaitable = boost::asio::experimental::as_tuple(boost::asio::use_awaitable);
using namespace std::literals::chrono_literals;
using namespace boost::asio::experimental::awaitable_operators;

awaitable<void> echo(tcp::socket socket)
{
    try
    {
        char data[2000];
        for (;;)
        {
            steady_timer timer(co_await this_coro::executor);
            timer.expires_after(2ms);
            auto result = co_await(
                async_read(socket, buffer(data, 2000), use_nothrow_awaitable) || 
                timer.async_wait(use_nothrow_awaitable)
            );

            if (result.index() == 1) {
                // 超时,无处理
            } else {
                auto [e, n] = std::get<0>(result);
                if (!e) {
                    if (n > 0) {
                        auto [write_e, write_n] = co_await async_write(
                            socket, buffer(data, n), use_nothrow_awaitable
                        );
                        if (write_e) {
                            std::cout << "Write error: " << write_e.message() << std::endl;
                            break;
                        }
                    }
                } else {
                    std::cout << "Read error: " << e.message() << std::endl;
                    break;
                }
            }
        }
    }
    catch (const std::exception& e)
    {
        std::printf("echo Exception: %s\n", e.what());
    }
}

awaitable<void> listener()
{
    auto executor = co_await this_coro::executor;
    tcp::acceptor acceptor(executor, {tcp::v4(), 5555});
    for (;;)
    {
        tcp::socket socket = co_await acceptor.async_accept(use_awaitable);
        co_spawn(executor, echo(std::move(socket)), detached);
    }
}

int main(int argc, char* argv[])
{
    try
    {
        thread_pool pol(8);
        boost::asio::signal_set signals(pol, SIGINT, SIGTERM);
        signals.async_wait([&](auto, auto) { pol.stop(); });

        co_spawn(pol, listener(), detached);

        pol.wait();
    }
    catch (const std::exception& e)
    {
        std::printf("Exception: %s\n", e.what());
    }
}

修复后的客户端代码

#include <boost/asio.hpp>
#include <boost/asio/thread_pool.hpp>
#include <boost/asio/experimental/awaitable_operators.hpp>
#include <boost/asio/experimental/as_tuple.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/signal_set.hpp>
#include <cstdio>

using boost::asio::ip::tcp;
using boost::asio::awaitable;
using boost::asio::co_spawn;
using boost::asio::detached;
using boost::asio::steady_timer;
using boost::asio::use_awaitable;
using std::chrono::steady_clock;
using boost::asio::thread_pool;
using boost::asio::buffer;
namespace this_coro = boost::asio::this_coro;

constexpr auto use_nothrow_awaitable = boost::asio::experimental::as_tuple(boost::asio::use_awaitable);
using namespace std::literals::chrono_literals;
thread_pool io_context(3);

awaitable<void> timeout(steady_clock::duration duration)
{
    steady_timer timer(co_await this_coro::executor);
    timer.expires_after(duration);
    co_await timer.async_wait(use_nothrow_awaitable);
}

awaitable<void> echo3(tcp::socket socket)
{
    try
    {
        char data[4024];
        for (;;)
        {
            co_await timeout(2ms);
            auto [e, n] = co_await async_write(
                socket, buffer(data, 2000), use_nothrow_awaitable
            );
            if (e) {
                std::printf("Write error: %s\n", e.message().c_str());
                break;
            }
        }
    }
    catch (const std::exception& e)
    {
        std::printf("echo3 Exception: %s\n", e.what());
    }
}

awaitable<void> listener()
{
    try
    {
        auto executor = co_await this_coro::executor;
        tcp::socket socket(executor);
        
        tcp::resolver resolver(executor);
        auto endpoints = resolver.resolve("127.0.0.1", "5555");
        auto [connect_e] = co_await async_connect(socket, endpoints, use_nothrow_awaitable);
        
        if (connect_e) {
            std::printf("Connect error: %s\n", connect_e.message().c_str());
            co_return;
        }
        
        co_spawn(executor, echo3(std::move(socket)), detached);
    }
    catch (const std::exception& e)
    {
        std::printf("listener Exception: %s\n", e.what());
    }
}

int main()
{
    try
    {
        boost::asio::signal_set signals(io_context, SIGINT, SIGTERM);
        signals.async_wait([&](auto, auto) { io_context.stop(); });

        for (size_t i = 0; i < 3; i++)
        {
            co_spawn(io_context, listener(), detached);
        }

        io_context.wait();
    }
    catch (const std::exception& e)
    {
        std::printf("main Exception: %s\n", e.what());
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 16:04:59