如何基于非Boost版Asio实现自定义异步操作?
实现方案
核心思路
只需要做两处最小改动即可兼容所有Asio CompletionToken,改造成本极低,团队易维护:
- 将原有存储
std::promise的集合,替换为存储统一签名的完成回调函数 - 用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
相关产品推荐
相关产品推荐

