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

如何等待单个任务完成且不取消其他Boost Asio任务?

问题:在Boost.Asio中等待单个transfer任务完成且不取消其他任务

假设我运行多个transfer任务(数量为三个或更多),如何在bidirectional_transfer方法中等待单个任务完成,同时不取消仍在运行的其他任务?

当前代码实现如下:

#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/asio/experimental/promise.hpp>
#include <boost/asio/experimental/use_promise.hpp>
#include <boost/asio/experimental/awaitable_operators.hpp>
#include <iostream>
#include <iomanip>

namespace asio = boost::asio;
namespace ssl = boost::asio::ssl;
using tcp = asio::ip::tcp;

asio::awaitable<void> transfer(ssl::stream<tcp::socket>& from, ssl::stream<tcp::socket>& to);

asio::awaitable<void> bidirectional_transfer(ssl::stream<tcp::socket>& client_ssl, ssl::stream<tcp::socket>& server_ssl)
{
    auto ex = co_await boost::asio::this_coro::executor;

    auto [order, ex0, ex1] =
        co_await asio::experimental::make_parallel_group(
            asio::co_spawn(ex, transfer(client_ssl, server_ssl), boost::asio::deferred),
            asio::co_spawn(ex, transfer(server_ssl, client_ssl), boost::asio::deferred)
        ).async_wait(
            asio::experimental::wait_for_one_success(),
            asio::deferred
        );

    // 此时仍有一个任务在运行,需要在这里等待它完成
}

我需要类似wait_for_one_success的等待策略功能,但不会取消仍在运行的任务。当前实现中我使用了wait_for_one_error或operator &&。

编辑1:
任务无法比co_spawn存在更久,因为它按值接收awaitable:

template <typename Executor, typename AwaitableExecutor,
    BOOST_ASIO_COMPLETION_TOKEN_FOR(
      void(std::exception_ptr)) CompletionToken>
inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
    CompletionToken, void(std::exception_ptr))
co_spawn(const Executor& ex,
    awaitable<void, AwaitableExecutor> a, CompletionToken&& token,
    constraint_t<
      (is_executor<Executor>::value || execution::is_executor<Executor>::value)
        && is_convertible<Executor, AwaitableExecutor>::value
    >)

解决方案

Boost.Asio的parallel_group默认在触发等待策略(比如wait_for_one_success)时会取消未完成的任务,要实现“等待第一个任务完成但不取消其他任务”的需求,需要避免使用parallel_group的内置取消机制,转而手动跟踪任务状态:

方法1:使用Promise跟踪第一个完成的任务,后续等待剩余任务

为每个transfer任务绑定完成回调,第一个任务完成时通知主协程,之后主协程再等待所有剩余任务完成:

asio::awaitable<void> bidirectional_transfer(ssl::stream<tcp::socket>& client_ssl, ssl::stream<tcp::socket>& server_ssl)
{
    auto ex = co_await boost::asio::this_coro::executor;
    asio::experimental::promise<void> first_done_promise;
    auto first_done = first_done_promise.get_future();

    // 存储所有任务的awaitable,用于后续等待
    std::vector<asio::awaitable<void>> tasks;

    // 启动第一个transfer任务,完成时触发promise
    tasks.push_back([&]() -> asio::awaitable<void> {
        try {
            co_await transfer(client_ssl, server_ssl);
            first_done_promise.set_value();
        } catch (...) {
            first_done_promise.set_exception(std::current_exception());
        }
    }());

    // 启动第二个transfer任务,仅在promise未触发时设置
    tasks.push_back([&]() -> asio::awaitable<void> {
        try {
            co_await transfer(server_ssl, client_ssl);
            first_done_promise.set_value_noexcept();
        } catch (...) {
            first_done_promise.set_exception_noexcept(std::current_exception());
        }
    }());

    // 可添加更多任务...

    // 等待第一个任务完成
    co_await first_done;

    // 等待所有剩余任务完成,处理可能的异常
    for (auto& task : tasks) {
        try {
            co_await std::move(task);
        } catch (const std::exception& e) {
            std::cerr << "剩余任务异常: " << e.what() << std::endl;
        }
    }
}

方法2:使用asio::experimental::when_any(Boost 1.78+)

Boost 1.78及以上版本提供的when_any可以等待第一个awaitable完成,且不会取消其他任务,之后再等待剩余任务:

#include <boost/asio/experimental/when_any.hpp>

asio::awaitable<void> bidirectional_transfer(ssl::stream<tcp::socket>& client_ssl, ssl::stream<tcp::socket>& server_ssl)
{
    auto ex = co_await boost::asio::this_coro::executor;

    auto task1 = asio::co_spawn(ex, transfer(client_ssl, server_ssl), asio::deferred);
    auto task2 = asio::co_spawn(ex, transfer(server_ssl, client_ssl), asio::deferred);
    // 添加更多任务...

    // 等待第一个任务完成
    auto [index, result] = co_await asio::experimental::when_any(task1, task2);

    // 等待剩余任务完成
    if (index == 0) {
        try { co_await std::move(task2); } catch (...) {}
        // 若有更多任务,依次等待
    } else {
        try { co_await std::move(task1); } catch (...) {}
        // 若有更多任务,依次等待
    }
}

关键注意事项

  • 必须等待所有任务完成,否则co_spawn持有的awaitable会提前销毁,导致未定义行为。
  • 需处理任务抛出的异常,避免程序崩溃或资源泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 03:07:40