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

如何在Boost.Asio协程中自定义协程状态?

实现Boost.Asio协程的自定义线程局部状态

一、为什么不能直接扩展yield_context?

Boost.Asio的yield_context本质是轻量级控制适配器,设计目标仅传递协程调度必需的核心信息(cancellation_slot、executor),并不承载用户自定义状态。从源码逻辑看,yield_context内部持有对ASIO私有coroutine_context的引用,这个内部结构负责协程栈管理与调度,对外完全不透明——用户无法直接修改或扩展它的成员。强行继承或包装yield_context会破坏ASIO内部的协程调度逻辑,因为绝大多数异步操作的重载都是针对原生yield_context设计的。

二、可行方案1:线程局部存储+自定义协程启动包装

核心思路

ASIO协程虽无全局可见的唯一标识,但可通过线程局部指针绑定协程状态,在协程启动时创建状态实例,执行时让当前协程直接访问,退出时自动清理。

具体实现

1. 定义自定义状态类

struct CoroutineState {
    std::vector<std::string> log_scopes; // 日志命名作用域列表
    // 可按需添加其他状态字段
};

2. 全局容器与线程局部指针

#include <unordered_map>
#include <memory>
#include <thread>
#include <mutex>
#include <boost/asio.hpp>

// 全局容器存储所有协程状态,用状态实例地址作为唯一键
std::unordered_map<const void*, std::unique_ptr<CoroutineState>> g_coroutine_states;
std::mutex g_states_mutex; // 线程安全锁
// 线程局部指针,指向当前执行协程的状态
thread_local CoroutineState* t_current_state = nullptr;

3. 自定义协程启动包装

替代原生boost::asio::spawn,自动为新协程绑定状态:

template <typename Executor, typename Coroutine>
void spawn_with_state(const Executor& ex, Coroutine&& coro) {
    auto state = std::make_unique<CoroutineState>();
    auto state_ptr = state.get();

    // 存入全局容器(线程安全)
    {
        std::lock_guard<std::mutex> lock(g_states_mutex);
        g_coroutine_states.emplace(state_ptr, std::move(state));
    }

    boost::asio::spawn(ex, [state_ptr, coro = std::forward<Coroutine>(coro)](boost::asio::yield_context yield) mutable {
        // 保存旧状态,切换为当前协程状态
        CoroutineState* old_state = t_current_state;
        t_current_state = state_ptr;

        try {
            // 执行用户协程逻辑
            coro(yield);
        } catch (...) {
            // 异常时恢复旧状态
            t_current_state = old_state;
            throw;
        }

        // 协程退出,恢复旧状态并清理全局容器
        t_current_state = old_state;
        std::lock_guard<std::mutex> lock(g_states_mutex);
        g_coroutine_states.erase(state_ptr);
    });
}

4. 日志场景示例

void log_with_scope(const std::string& msg) {
    if (t_current_state) {
        std::string scope_prefix;
        for (const auto& scope : t_current_state->log_scopes) {
            scope_prefix += "[" + scope + "]";
        }
        std::cout << scope_prefix << " " << msg << std::endl;
    } else {
        std::cout << msg << std::endl;
    }
}

void my_coroutine(boost::asio::yield_context yield) {
    t_current_state->log_scopes.push_back("my_coroutine");
    log_with_scope("Start working");

    // 嵌套启动协程
    spawn_with_state(yield.get_executor(), [](boost::asio::yield_context yield) {
        t_current_state->log_scopes.push_back("nested_coroutine");
        log_with_scope("Nested coroutine running");
    });

    log_with_scope("Finish working");
    t_current_state->log_scopes.pop_back();
}

三、更优雅的方案2:利用cancellation_slot关联状态

每个协程的cancellation_slot是唯一的,且其生命周期与协程绑定,可直接用它关联自定义状态,无需自定义spawn包装:

实现代码

#include <unordered_map>
#include <memory>
#include <mutex>
#include <boost/asio.hpp>

struct CoroutineState {
    std::vector<std::string> log_scopes;
};

// 获取当前协程的状态
CoroutineState& get_current_coroutine_state(boost::asio::yield_context yield) {
    static std::unordered_map<const boost::asio::cancellation_slot*, std::unique_ptr<CoroutineState>> state_map;
    static std::mutex map_mutex;

    auto& slot = yield.get_cancellation_slot();
    std::lock_guard<std::mutex> lock(map_mutex);

    auto it = state_map.find(&slot);
    if (it == state_map.end()) {
        auto state = std::make_unique<CoroutineState>();
        it = state_map.emplace(&slot, std::move(state)).first;
        // 协程结束时(slot销毁)自动清理状态
        slot.assign([&slot, &state_map, &map_mutex]() {
            std::lock_guard<std::mutex> lock(map_mutex);
            state_map.erase(&slot);
        });
    }
    return *it->second;
}

// 使用示例
void my_coroutine(boost::asio::yield_context yield) {
    auto& state = get_current_coroutine_state(yield);
    state.log_scopes.push_back("my_coroutine");
    // 日志逻辑...
}

四、关键注意事项

  • 线程安全:全局容器必须加锁,避免多线程调度协程时的竞态问题。
  • 嵌套协程:线程局部指针方案需保存并恢复旧状态,防止嵌套协程的状态互相覆盖。
  • 内存泄漏:利用cancellation_slot的回调或lambda生命周期自动清理状态,避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 23:50:28