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

如何使用boost::log named_scope并兼容boost::asio::yield_context及协程?

解决boost::log named_scope与boost::asio协程(yield_context)兼容问题

问题根源

BOOST_LOG_NAMED_SCOPE 默认依赖**线程局部存储(TLS)**维护作用域栈,但协程共享线程执行环境,切换协程时线程TLS不会同步切换;同时协程栈的销毁顺序与执行顺序无关,导致默认的作用域记录会出现混乱、与实际执行逻辑不符的情况。

核心解决方案

将作用域栈与协程上下文绑定,而非线程。通过自定义boost::log的named_scope存储接口,为每个协程维护独立的作用域栈,保证协程切换时作用域记录的正确性。

实现步骤与代码示例

1. 自定义协程专属的作用域栈存储类

实现boost::log::named_scope::scope_stack_interface接口,利用协程上下文作为唯一标识,维护每个协程独立的作用域栈:

#include <boost/log/core.hpp>
#include <boost/log/attributes/named_scope.hpp>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/coroutine2/all.hpp>
#include <memory>

// 协程作用域栈节点定义
struct ScopeNode {
    std::string name;
    const char* file;
    unsigned int line;
    ScopeNode* next = nullptr;
};

// 协程属性的唯一标识key
struct CoroutineScopeStackKey {};

// 自定义作用域栈存储,绑定到协程上下文
class CoroutineScopeStack : public boost::log::named_scope::scope_stack_interface {
public:
    // 推送新作用域
    void push(const char* name, const char* file, unsigned int line) override {
        // 获取当前协程的作用域栈,不存在则初始化为nullptr
        auto& stack = boost::coroutines2::get_attribute<ScopeNode*>(CoroutineScopeStackKey{}, nullptr);
        stack = new ScopeNode{name, file, line, stack};
    }

    // 弹出作用域(RAII自动触发)
    void pop() override {
        auto& stack = boost::coroutines2::get_attribute<ScopeNode*>(CoroutineScopeStackKey{}, nullptr);
        if (stack) {
            auto temp = stack;
            stack = stack->next;
            delete temp;
        }
    }

    // 获取当前栈顶作用域
    boost::log::named_scope::scope_entry get_current() const override {
        auto& stack = boost::coroutines2::get_attribute<ScopeNode*>(CoroutineScopeStackKey{}, nullptr);
        if (stack) {
            return {stack->name.c_str(), stack->file, stack->line};
        }
        return {};
    }

    // 遍历作用域栈(用于日志格式化)
    void iterate(boost::log::named_scope::scope_stack_iterator const& visitor) const override {
        auto& stack = boost::coroutines2::get_attribute<ScopeNode*>(CoroutineScopeStackKey{}, nullptr);
        for (auto node = stack; node; node = node->next) {
            if (!visitor.visit(node->name.c_str(), node->file, node->line)) {
                break;
            }
        }
    }
};

// 作用域栈工厂类,用于创建自定义存储实例
class CoroutineScopeStackFactory : public boost::log::named_scope::scope_stack_factory {
public:
    boost::log::named_scope::scope_stack_interface* create_stack() override {
        return new CoroutineScopeStack();
    }
};

2. 初始化日志与协程环境

在程序启动时设置自定义作用域栈工厂,确保所有协程使用专属的作用域记录:

int main() {
    // 初始化日志,添加named_scope全局属性
    boost::log::add_common_attributes();
    boost::log::core::get()->add_global_attribute("Scope", boost::log::attributes::named_scope());

    // 设置自定义协程作用域栈工厂
    boost::log::named_scope::set_scope_stack_factory(std::make_shared<CoroutineScopeStackFactory>());

    boost::asio::io_context io_ctx;

    // 启动多个协程模拟并发场景
    for (int i = 0; i < 3; ++i) {
        boost::asio::spawn(io_ctx, [i](boost::asio::yield_context yield) {
            BOOST_LOG_NAMED_SCOPE((std::string("coroutine_") + std::to_string(i)).c_str());
            
            // 模拟异步操作触发协程切换
            boost::asio::steady_timer timer(io_ctx);
            timer.expires_after(std::chrono::milliseconds(50));
            timer.async_wait(yield);

            BOOST_LOG_TRIVIAL(info) << "协程" << i << "执行中";

            {
                BOOST_LOG_NAMED_SCOPE("inner_task");
                BOOST_LOG_TRIVIAL(info) << "协程" << i << "进入子任务";
            }

            BOOST_LOG_TRIVIAL(info) << "协程" << i << "退出主作用域";
        });
    }

    io_ctx.run();
    return 0;
}

关键说明

  1. 协程绑定逻辑:通过boost::coroutines2::get_attribute获取当前协程的专属作用域栈,每个协程的栈完全独立,不受其他协程切换影响。
  2. RAII正确性:BOOST_LOG_NAMED_SCOPE的RAII对象存储在协程栈上,只有当协程栈销毁时才会触发pop操作,保证作用域生命周期与协程执行逻辑严格一致。
  3. 性能优化:使用协程属性替代全局映射,无需互斥锁同步,避免线程竞争开销。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 05:25:16