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

如何用Boost.Asio+C++20协程实现非阻塞耗时计算任务?

用Boost.Asio实现需求的最优低开销方案

一、耗时计算任务的主动让出与空闲恢复

要让耗时计算不阻塞io_context线程,同时在执行一段计算后主动让出,等io_context无其他任务时再继续,最省资源的方式就是用**asio::defer的可等待版本**。

asio::defer的作用是把后续计算任务放到执行器的延迟队列里,只有当io_context当前没有其他待处理任务时,才会调度执行这段计算。完全不需要定时器,无额外开销,还能保证io_context优先处理其他IO任务或高优先级任务,只有闲下来才继续计算。

在计算循环里,每完成一块计算后,用co_await asio::defer(executor)就能让协程挂起,把后续计算推迟到io_context空闲时执行。

二、一对多的协程等待事件实现

需要自定义一个CoroEvent类,支持多个协程异步等待结果,计算完成时一次性唤醒所有等待的协程,而且之后再进来的等待请求能直接拿到结果,不用挂起。

核心逻辑

  • 用容器存储所有等待的回调函数;
  • 用布尔值标记计算是否完成;
  • 调用async_wait时,如果计算已完成就直接触发回调,未完成则把回调加入等待队列;
  • 计算完成后调用send,遍历所有回调并触发,同时标记事件已完成。

完整可运行代码

#include <boost/asio.hpp>
#include <vector>
#include <functional>
#include <iostream>
#include <mutex>

namespace asio = boost::asio;

// 支持一对多等待的协程事件类
class CoroEvent {
public:
    explicit CoroEvent(asio::any_io_executor executor)
        : executor_(std::move(executor))
        , signaled_(false) {}

    // 异步等待事件,兼容asio::use_awaitable
    template <typename CompletionToken>
    auto async_wait(CompletionToken&& token) {
        return asio::async_initiate<CompletionToken, void()>(
            [this](auto handler) {
                std::lock_guard<std::mutex> lock(mutex_);
                if (signaled_) {
                    // 事件已触发,直接调度回调
                    asio::post(executor_, std::move(handler));
                } else {
                    // 事件未触发,将回调加入等待队列
                    handlers_.push_back(std::move(handler));
                }
            },
            token
        );
    }

    // 触发事件,唤醒所有等待的协程
    void send() {
        std::vector<std::function<void()>> handlers;
        {
            std::lock_guard<std::mutex> lock(mutex_);
            if (signaled_) return;
            signaled_ = true;
            handlers.swap(handlers_); // 转移队列所有权,避免持锁时执行回调
        }
        // 逐个调度等待的回调
        for (auto& handler : handlers) {
            asio::post(executor_, std::move(handler));
        }
    }

    // 重置事件(如需重复使用)
    void reset() {
        std::lock_guard<std::mutex> lock(mutex_);
        signaled_ = false;
        handlers_.clear();
    }

private:
    asio::any_io_executor executor_;
    std::mutex mutex_;
    bool signaled_ = false;
    std::vector<std::function<void()>> handlers_;
};

// 模拟耗时计算的结果类型
struct MyReturnType {
    int total = 0;
    // 模拟单次计算操作
    void compute(size_t idx) {
        total += static_cast<int>(idx % 100);
    }
};

MyReturnType calc_result;

// 耗时计算协程
asio::awaitable<void> long_running_calc(asio::any_io_executor executor) {
    // 双层循环模拟大量计算任务
    for (size_t i = 0; i < 1000; ++i) {
        for (size_t j = 0; j < 1000; ++j) {
            calc_result.compute(i * 1000 + j);
        }
        // 让出执行权,等待io_context空闲后继续
        co_await asio::defer(executor);
    }
    // 计算完成,触发事件通知所有等待者
    static CoroEvent finish_event(co_await asio::this_coro::executor);
    finish_event.send();
}

// 获取计算结果的协程
asio::awaitable<MyReturnType> get_calc_result() {
    static CoroEvent finish_event(co_await asio::this_coro::executor);
    co_await finish_event.async_wait(asio::use_awaitable);
    co_return calc_result;
}

// 模拟其他IO任务,验证io_context不会被阻塞
asio::awaitable<void> sample_io_task(int task_id) {
    for (int i = 0; i < 5; ++i) {
        std::cout << "IO任务 " << task_id << " 正在运行\n";
        // 模拟IO等待(实际场景替换为真实异步IO操作)
        co_await asio::steady_timer(co_await asio::this_coro::executor, std::chrono::milliseconds(100)).async_wait(asio::use_awaitable);
    }
}

int main() {
    asio::io_context io_ctx;

    // 启动计算任务
    asio::co_spawn(io_ctx, long_running_calc(io_ctx.get_executor()), asio::detached);

    // 启动3个等待计算结果的协程
    for (int i = 0; i < 3; ++i) {
        asio::co_spawn(io_ctx, [i]() -> asio::awaitable<void> {
            auto result = co_await get_calc_result();
            std::cout << "等待者 " << i << " 获取到结果:" << result.total << "\n";
        }, asio::detached);
    }

    // 启动2个IO任务,验证io_context的响应性
    for (int i = 0; i < 2; ++i) {
        asio::co_spawn(io_ctx, sample_io_task(i), asio::detached);
    }

    // 运行io_context
    io_ctx.run();

    return 0;
}

三、方案优势

  • 零额外开销:无需定时器,完全依托asio执行器调度,没有定时器的创建、销毁和轮询成本;
  • io_context始终响应:计算任务仅在io_context空闲时执行,确保其他IO任务能被及时处理;
  • 高效一对多等待:事件触发时一次性唤醒所有等待协程,后续等待请求直接返回结果,无需挂起。

其他协程库的类似机制

  • libunifex:提供manual_event类,支持多协程等待,触发时唤醒所有等待者;
  • CppCoro:通过event类实现相同的多协程等待语义。

内容的提问来源于stack exchange,提问作者Fabian Keßler

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 02:45:44