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

为带虚回调的硬件交互旧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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 10:44:52