PAYATHON 2026

Flecs 多线程创建实体时触发 table.c 内部断言怎么办?

数据阿明

结论

这段代码使用了 Flecs 的 stage,但没有显式开启多线程只读阶段。对于应用自行管理线程、通过 get_stage(id) 写入的场景,应在线程启动前调用 readonly_begin(true),等所有线程结束后调用 readonly_end(),再检查实体。

get_stage(id) 会为线程提供独立的命令上下文,因此每个线程都应使用不同的 stage。set_stage_count(4) 配置了 4 个 stage,但这本身不会启动并行阶段。官方文档说明,stage 上的操作会排队,并在 stage 合并时应用到 world。Flecs Commands 文档

调整代码

int main() {
    flecs::world world;

    world.set_stage_count(4);
    world.component<Position>();
    world.component<Velocity>();

    // 在线程启动前进入多线程只读阶段。
    world.readonly_begin(true);

    std::vector<std::thread> threads;
    for (int i = 0; i < 4; i++) {
        threads.emplace_back(worker, std::ref(world), i);
    }
    for (auto& t : threads) {
        t.join();
    }

    // 结束只读阶段,并让 Flecs 处理各 stage 的排队操作。
    world.readonly_end();

    world.each<Position>([](flecs::entity e, Position& p) {
        std::cout << "Entity " << e.id()
                  << " Position(" << p.x << "," << p.y << ")\n";
    });
}

readonly_begin(true) 应在线程启动前调用,readonly_end() 则应在线程全部 join() 后调用。Flecs 的 C++ world API 将 readonly_begin 定义为进入只读模式,并指出 merge() 只能在 staging 未启用时调用,例如 progress() 结束后或 readonly_end() 之后。Flecs world API

按这个生命周期使用时,通常不需要在 readonly_end() 后立即再调用 world.merge()。如果所用 Flecs 版本或具体工作流要求手动合并,应在 readonly_end() 之后调用,并核对该版本的 API 文档。

还要检查的地方

  • 每个并行线程都必须使用自己的 stage。示例中的线程 ID 为 0 到 3,对应配置的 4 个 stage;不要让多个线程同时操作同一个 stage。
  • 在线程启动前完成组件注册等 world 结构配置。只读阶段运行期间,避免从主 world 直接执行普通写操作。
  • 这条断言说明内部表记录与表状态不一致,但仅凭日志无法确定唯一根因。如果按上述阶段边界调整后仍然崩溃,还需要确认 Flecs 的具体版本、完整调用栈,以及是否有其他线程同时访问 world 或 stage。

备注:内容仅供参考。