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

如何基于非Boost版Asio实现自定义异步操作?

实现方案

核心思路

只需要做两处最小改动即可兼容所有Asio CompletionToken,改造成本极低,团队易维护:

  1. 将原有存储std::promise的集合,替换为存储统一签名的完成回调函数
  2. 用Asio 1.19+原生提供的asio::async_initiate完成不同类型CompletionToken的自动适配,无需手写多重重载

完整代码实现

异步接口实现

首先定义完成回调的统一签名,再基于async_initiate封装接口:

#include <asio.hpp>
// 引入你项目中使用的json库、套接字管理模块等头文件

// 定义RPC异步操作的完成handler固定签名:第一个参数是错误码,第二个是返回结果
using RpcCompletionSig = void(std::error_code, std::string);

template <typename CompletionToken>
auto async_json_rpc(std::string method, json::array args, CompletionToken&& token)
{
    // asio::async_initiate会自动适配所有类型的CompletionToken
    return asio::async_initiate<CompletionToken, RpcCompletionSig>(
        // 异步操作实际发起逻辑
        [](auto handler, std::string method, json::array args) {
            // 1. 生成唯一请求ID(复用你原有逻辑即可)
            uint64_t req_id = generate_next_request_id();
            // 2. 将handler存入待处理集合,替换原有存promise的逻辑
            get_pending_handler_map().emplace(req_id, std::move(handler));
            // 3. 构造请求报文(复用原有逻辑)
            json req_body = {
                {"method", std::move(method)},
                {"params", std::move(args)},
                {"id", req_id}
            };
            // 4. 发送RPC请求(复用原有套接字发送逻辑)
            send_socket_data(req_body.dump());
        },
        token, // 透传用户传入的CompletionToken
        std::move(method),
        std::move(args)
    );
}

响应触发逻辑(等价于原有promise.set_value)

你原有收到响应后的处理逻辑,只需要把调用set_value的部分换成调用存储的handler即可:

// 套接字收到响应后的回调逻辑
void on_recv_rpc_response(uint64_t req_id, std::string result, std::error_code ec = {})
{
    auto& pending_map = get_pending_handler_map();
    auto iter = pending_map.find(req_id);
    if (iter == pending_map.end()) {
        return; // 超时/重复请求直接丢弃
    }
    // 先把handler从集合取出再执行,避免重入问题
    auto handler = std::move(iter->second);
    pending_map.erase(iter);
    // 直接调用handler即可,Asio会自动处理后续逻辑:
    // 传入lambda则直接执行、传入use_future则自动set_value、传入yield_context则自动唤醒协程
    std::move(handler)(ec, std::move(result));
}

多种用法示例

完全和Asio原生异步接口的使用方式一致:

1. Lambda回调用法

async_json_rpc("subtract", json::array{42, 23}, [](std::error_code ec, std::string result) {
    if (!ec) {
        std::cout << "调用结果:" << result << std::endl;
    }
});

2. 兼容原有future用法

std::future<std::string> fut = async_json_rpc("subtract", json::array{42, 23}, asio::use_future);
std::string res = fut.get();

3. Asio协程用法

asio::spawn(io_context, [](asio::yield_context yield) {
    std::error_code ec;
    std::string result = async_json_rpc("subtract", json::array{42, 23}, yield[ec]);
    if (!ec) {
        std::cout << "协程调用结果:" << result << std::endl;
    }
});

维护注意事项

  • 所有逻辑都复用你原有实现,没有新增复杂的Asio自定义对象、操作符等内容,学习成本极低
  • 如果需要让回调在用户指定的io_context线程执行,不要直接在套接字收消息线程调用handler,改成asio::post(user_io_ctx, std::bind(std::move(handler), ec, std::move(result)))即可
  • 超时、连接断开等异常场景,直接找到对应ID的handler,调用时传入对应错误码即可,逻辑和正常响应完全一致

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 13:06:07