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

如何从th2线程向th1线程启动Boost fiber并加入其调度队列

问题根因

Boost.Fiber 是线程绑定的协程库:每个线程的调度器实例、fiber 队列都是线程本地存储(TLS)隔离的,你在 th2 里直接调用创建 fiber 的代码,新 fiber 会被绑定到 th2 的调度器上下文——但你根本没在 th2 跑 fiber 调度循环,就算你给 th1 的调度器加了锁也没用,因为创建 fiber 时压根不会跨线程往 th1 的队列里塞东西。
另外你代码里每次调用launchFiber都执行boost::fibers::use_scheduling_algorithm< priority_scheduler >()是错误用法,这个接口只需要在对应线程启动fiber调度循环前调用一次,重复调用会重复初始化TLS上的调度器实例,直接触发未定义行为。

正确实现方案

要实现从 th2 往 th1 投递 fiber,核心是用 Boost.Fiber 内置的线程安全通道做跨线程任务传递,不要自己跨线程直接操作调度器队列:

  • 首先在 th1 初始化调度器的时候,绑定一个跨线程可访问的无锁通道,专门用来接收外部线程提交的fiber任务
  • th2 不需要直接创建绑定到th1的fiber,而是把待执行的fiber逻辑、优先级、名称打包,通过通道投递到th1
  • th1的调度器在每次调度轮转时,先拉取通道里的待启动任务,在th1本地上下文创建fiber后加入就绪队列,再参与优先级调度

可运行代码示例

首先定义优先级属性、跨线程投递通道和适配后的优先级调度器:

#include <boost/fiber/all.hpp>
#include <mutex>
#include <queue>
#include <memory>
#include <functional>
#include <tuple>

// 原有优先级属性定义
struct priority_props : public boost::fibers::fiber_properties {
    int priority = 0;
    std::string name;
    priority_props(boost::fibers::fiber_properties::backptr_t bp)
        : boost::fibers::fiber_properties(bp) {}
    void set_priority(int p) {
        if (p != priority) {
            priority = p;
            notify();
        }
    }
};

// 全局跨线程投递通道:th2写、th1读,Boost.Fiber自带的通道原生线程安全
using pending_fiber_pkg = std::tuple<std::function<void()>, int, std::string>;
boost::fibers::unbounded_channel<pending_fiber_pkg> g_fiber_submit_chan;

// 修正后的优先级调度器
class priority_scheduler {
private:
    mutable std::mutex mtx_;
    // 就绪队列:存上下文指针、优先级、fiber名
    std::queue<std::tuple<boost::fibers::context*, int, std::string>> ready_queue_;
    boost::fibers::context* main_ctx_ = nullptr;

public:
    priority_scheduler() = default;
    priority_scheduler(const priority_scheduler&) = delete;
    priority_scheduler& operator=(const priority_scheduler&) = delete;

    // 调度器必填接口:新fiber创建完成时的唤醒回调
    void awakened(boost::fibers::context* ctx, boost::fibers::fiber_properties props) noexcept {
        auto& p = props.get<priority_props>();
        std::lock_guard<std::mutex> lk(mtx_);
        ready_queue_.emplace(ctx, p.priority, p.name);
    }

    // 调度器必填接口:选择下一个要运行的fiber
    boost::fibers::context* pick_next() noexcept {
        // 先拉取所有外部线程投递的待创建任务
        pending_fiber_pkg pkg;
        while (g_fiber_submit_chan.try_pop(pkg)) {
            auto [func, prio, name] = std::move(pkg);
            // 注意:这里是在th1上下文创建fiber,会自动绑定到th1的调度器
            boost::fibers::fiber f(boost::fibers::launch::post, std::move(func));
            auto& props = f.properties<priority_props>();
            props.set_priority(prio);
            props.name = std::move(name);
            f.detach();
        }

        std::lock_guard<std::mutex> lk(mtx_);
        if (ready_queue_.empty()) {
            return main_ctx_;
        }
        // 这里简化逻辑直接取队首,你可以自行实现按优先级排序取最高优先级任务的逻辑
        auto [ctx, prio, name] = ready_queue_.front();
        ready_queue_.pop();
        return ctx;
    }

    // 调度器必填接口:判断是否存在待运行fiber
    bool has_ready_fibers() const noexcept {
        std::lock_guard<std::mutex> lk(mtx_);
        // 必须把通道里待投递的任务算进去,否则调度循环会提前退出
        return !ready_queue_.empty() || !g_fiber_submit_chan.empty();
    }

    // 调度器必填接口:绑定线程主上下文
    void set_main_ctx(boost::fibers::context* ctx) noexcept {
        main_ctx_ = ctx;
    }

    // 调度器绑定的属性类型声明
    using properties_t = priority_props;
};

th1线程的初始化和调度循环逻辑:

void th1_entry() {
    // 仅在th1启动时设置一次调度器,禁止重复调用
    boost::fibers::use_scheduling_algorithm<priority_scheduler>();

    // 在这里启动th1原有的常驻业务fiber即可
    // 例:boost::fibers::fiber(original_th1_fiber_func).detach();

    // th1调度主循环
    while (true) {
        // 没有就绪任务也没有待投递任务时,阻塞等待新任务避免CPU空转
        if (!boost::this_fiber::has_ready_fibers() && g_fiber_submit_chan.empty()) {
            pending_fiber_pkg pkg;
            g_fiber_submit_chan.pop(pkg); // 阻塞直到th2投递新任务
            g_fiber_submit_chan.push(std::move(pkg)); // 塞回通道,下一轮调度会处理
        }
        // 让出执行权给调度器,切换到下一个就绪fiber运行
        boost::this_fiber::yield();
    }
}

跨线程安全的fiber启动接口,th2直接调用即可:

// 任意线程(包括th2)调用该接口,都可以把fiber投递到th1调度运行
void launchFiber(std::function<void()> fiber_func, int priority, std::string fiber_name) {
    g_fiber_submit_chan.push({
        std::move(fiber_func),
        priority,
        std::move(fiber_name)
    });
}
注意事项
  • 所有需要绑定到th1调度器的fiber,必须在th1的线程上下文中创建,绝对不要在th2里直接构造boost::fibers::fiber实例,否则会自动绑定到th2的TLS调度器,永远不会被th1调度
  • use_scheduling_algorithm仅能在对应调度线程启动时调用一次,重复调用会破坏TLS上的调度器状态
  • 直接使用Boost.Fiber自带的unbounded_channel做跨线程传递即可,它本身是无锁线程安全的,不需要额外加锁
  • 调度器的has_ready_fibers判断必须覆盖通道中的待投递任务,否则th1调度循环会在本地队列为空时直接退出,不会等待th2的新任务

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 06:42:19