为带虚回调的硬件交互旧API实现Asio异步包装(C++20协程)
实现符合Asio规范的异步write_async函数(适配旧硬件操作库)
问题背景
现有一款基于中断实现异步交互的旧硬件操作库,要求必须继承WriterBase类:调用writeMessage()后,只能通过重写纯虚函数wroteMessage()接收操作完成回调,无法直接通过函数参数传递处理逻辑。当前应用采用C++20协程结合Asio框架,需要实现支持co_await + asio::use_awaitable调用的write_async函数,此前尝试std::promise方案导致线程阻塞,参考Asio官方回调包装示例又因模板问题无法将处理函数传递至wroteMessage()中。
解决方案
核心思路是为每个异步操作关联Asio完成令牌,通过线程安全的映射将Packet指针与完成处理函数绑定,在wroteMessage()回调中触发Asio的完成逻辑,同时遵循Asio的线程模型将处理逻辑投递到io_context线程执行。
修改后的完整代码
// 旧库代码(不可修改) #include <array> #include <asio.hpp> #include <atomic> #include <memory> #include <thread> #include <chrono> #include <unordered_map> #include <mutex> #include <functional> using Packet = std::array<uint8_t, 1024>; class WriterBase { public: virtual ~WriterBase() {} void writeMessage(Packet *pkt) { using namespace std::chrono_literals; // 模拟硬件DMA传输完成后的中断回调 std::thread( [this, pkt] { std::this_thread::sleep_for(1s); bool ret = wroteMessage(pkt); if (ret) { delete pkt; } }) .detach(); } protected: virtual bool wroteMessage(Packet *pkt) = 0; }; // 自定义Writer类(可修改) class Writer : public WriterBase { private: asio::io_context& io_context_; std::mutex mutex_; // 映射Packet指针到对应的完成处理函数 std::unordered_map<Packet*, std::function<void()>> callbacks_; public: explicit Writer(asio::io_context& io_ctx) : io_context_(io_ctx) {} // 符合Asio规范的异步接口 template <typename CompletionToken> auto write_async(Packet* pkt, CompletionToken&& token) { // 初始化异步完成逻辑,自动适配CompletionToken asio::async_completion<CompletionToken, void()> init(token); { std::lock_guard<std::mutex> lock(mutex_); callbacks_[pkt] = std::move(init.completion_handler); } // 调用旧库的同步启动接口 writeMessage(pkt); // 返回适配后的结果(协程场景下会返回awaitable对象) return init.result.get(); } protected: bool wroteMessage(Packet* pkt) override { std::function<void()> handler; // 线程安全地取出对应的完成处理函数 { std::lock_guard<std::mutex> lock(mutex_); auto it = callbacks_.find(pkt); if (it != callbacks_.end()) { handler = std::move(it->second); callbacks_.erase(it); } } if (handler) { // 将完成处理函数投递到Asio的io_context线程执行,避免在中断线程处理业务 asio::post(io_context_, std::move(handler)); } // 返回true,由旧库负责释放Packet内存 return true; } }; // 目标使用场景:C++20协程+Asio int main_with_asio() { asio::io_context context; std::atomic<bool> terminate{false}; asio::co_spawn( context, [&terminate, &context]() -> asio::awaitable<void> { auto writer = std::make_unique<Writer>(context); while (!terminate.load()) { Packet *pkt = new Packet(); // 使用co_await等待异步操作完成 co_await writer->write_async(pkt, asio::use_awaitable); } }, asio::detached); context.run(); return 0; }
关键实现细节
- 线程安全的回调管理:用
std::mutex保护callbacks_映射表,避免write_async(协程线程)与wroteMessage(中断/后台线程)并发访问导致的数据竞争。 - Asio异步适配:通过
asio::async_completion自动处理不同类型的完成令牌(包括asio::use_awaitable、普通回调函数),这是Asio异步接口的标准实现方式。 - 线程模型兼容:使用
asio::post将完成处理函数投递到Asio的io_context线程执行,确保业务逻辑始终在Asio的线程池中处理,避免在中断线程中执行复杂逻辑引发问题。 - 资源管理:保持返回
true让旧库释放Packet,避免手动管理内存带来的泄漏风险。
内容的提问来源于stack exchange,提问作者MichaelO
相关产品推荐
相关产品推荐

