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

如何用C++20协程结合Boost.Asio等待多个CompletionToken?

背景

我正在使用Boost.Asio,调用多个异步函数后希望等待多个CompletionToken,例如async_read()和steady_timer::async_wait()。

我已通过Boost.Asio的无栈协程实现该逻辑,运行正常。

场景中有两个定时器,第一个定时器时长比第二个长,因此第二个会先触发并取消第一个。第一个定时器模拟async_read(),第二个作为其超时器。

无栈协程实现方案

#include <iostream>
#include <chrono>
#include <boost/asio.hpp>

namespace as = boost::asio;

#include <boost/asio/yield.hpp>

template <typename Executor>
struct my_app {
    my_app(Executor exe)
        :exe_{std::move(exe)},
         worker_{*this}
    {}

private:
    friend struct worker;
    struct worker {
        worker(my_app& ma)
            :ma_{ma}
        {
            // initiate coroutine
            (*this)(boost::system::error_code{}, std::string{});
        }
        void operator()(
            boost::system::error_code const& ec,
            std::string timer_str // to distinguish timer
        ) const {
            reenter(coro_) {
                std::cout << "start" << std::endl;

                yield {
                    // set two timers
                    // timer1 is a kind of receive emulation 
                    // timer2 is a receive timeout
                    ma_.t1_.expires_after(std::chrono::milliseconds(200));
                    ma_.t1_.async_wait(
                        as::append(
                            *this,
                            "timer1"
                        )
                    );
                    ma_.t2_.expires_after(std::chrono::milliseconds(100));
                    ma_.t2_.async_wait(
                        as::append(
                            *this,
                            "timer2"
                        )
                    );
                }
                if (ec) {
                    std::cout << timer_str << " " << ec.message() << std::endl;
                }
                else {
                    std::cout << timer_str << " fired" << std::endl;
                    if (timer_str == "timer2") {
                        ma_.t1_.cancel();
                    }
                }
            }
        }
        my_app& ma_;
        mutable as::coroutine coro_;
    };

    Executor exe_;
    as::steady_timer t1_{exe_};
    as::steady_timer t2_{exe_};
    worker worker_;
};

#include <boost/asio/unyield.hpp>

int main() {
    as::io_context ioc;
    my_app ma{ioc.get_executor()};
    ioc.run();
}
如何用C++20协程实现相同逻辑?

我尝试从无栈协程迁移到C++20协程,但找不到等待多个CompletionToken的方法,该如何实现?

已尝试方案

分步使用co_await

#include <iostream>
#include <chrono>
#include <boost/asio.hpp>

namespace as = boost::asio;

template <typename Executor>
as::awaitable<void>
proc(Executor exe) {
    as::steady_timer t1{exe, std::chrono::milliseconds{200}};
    as::steady_timer t2{exe, std::chrono::milliseconds{100}};
    // how to multiwait t1 and t2 ?
    co_await t1.async_wait(as::use_awaitable);
    std::cout << "timer fired1" << std::endl;
    co_await t2.async_wait(as::use_awaitable);
    std::cout << "timer fired2" << std::endl;
}

int main() {
    as::io_context ioc;
    as::co_spawn(ioc, proc(ioc.get_executor()), as::detached);
    ioc.run();
}

该方案无法正常工作,因为第二个定时器是在第一个触发后才设置的。

使用多个co_spawn

#include <iostream>
#include <chrono>
#include <boost/asio.hpp>

namespace as = boost::asio;

as::awaitable<void>
proc1(as::steady_timer& t1) {
    try {
        co_await t1.async_wait(as::use_awaitable);
        std::cout << "timer1 fired" << std::endl;
    }
    catch (std::runtime_error const& ec) {
        std::cout << "timer1 " << ec.what() << std::endl;
    }
}

as::awaitable<void>
proc2(as::steady_timer& t1, as::steady_timer& t2) {
    try {
        co_await t2.async_wait(as::use_awaitable);
        std::cout << "timer2 fired" << std::endl;
        t1.cancel();
    }
    catch (std::runtime_error const& ec) {
        std::cout << "timer2 " << ec.what() << std::endl;
    }
}

int main() {
    as::io_context ioc;

    as::steady_timer t1{ioc.get_executor(), std::chrono::seconds{2}};
    as::steady_timer t2{ioc.get_executor(), std::chrono::seconds{1}};

    as::co_spawn(ioc, proc1(t1), as::detached);
    as::co_spawn(ioc, proc2(t1, t2), as::detached);

    ioc.run();
}

该方案技术上可行,但存在问题,例如似乎无法在返回值为as::awaitable<T>的可等待函数中工作,我认为有更优方案。

环境
  • Boost 1.83.0
  • x86-64 clang 17.0.1

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 23:20:07