十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

为什么游戏引擎选纤程而不是线程:TaskScheduler纤程上下文切换原理剖析

为什么游戏引擎选纤程而不是线程:TaskScheduler纤程上下文切换原理剖析 为什么游戏引擎选纤程而不是线程TaskScheduler纤程上下文切换原理剖析【免费下载链接】TaskSchedulerCross-platform, fiber-based, multi-threaded task scheduler designed for video games.项目地址: https://gitcode.com/gh_mirrors/tasksch/TaskSchedulerTaskScheduler 是一个专为视频游戏设计的跨平台、纤程Fiber驱动的多线程任务调度器它把纤程而不是线程作为并行计算的基本执行单元用极低的纤程上下文切换开销解决了游戏多线程中等待子任务带来的锁竞争难题。本文带你剖析纤程与线程的本质区别以及 TaskScheduler 纤程上下文切换的底层实现原理帮助你在 10 分钟内理解游戏引擎任务调度的核心设计。 纤程 vs 线程一张表看懂切换开销差距游戏引擎每帧只有约 16ms 的预算却要调度成百上千个任务AI、物理、动画、渲染。线程虽然强大但有三宗罪对比维度线程Thread纤程Fiber切换方式内核态切换需系统调用用户态切换只换寄存器栈指针切换开销约 1~2 微秒以上约百纳秒级低一个数量级默认栈大小通常 1MB 起可低至 32KB按需分配调度时机由操作系统决定由纤程自己决定协作式等待同步需要互斥锁存在锁竞争挂起/恢复即可无需加锁关键差异在于等待语义一个父任务派发子任务后要等结果如果用线程就只能阻塞或加锁用纤程父任务只需让出执行权调度器立即切换去跑别的纤程等子任务全部完成后自动恢复父纤程——全程无锁、无内核参与。这也是顽皮狗Naughty Dog在经典 GDC 演讲《Parallelizing the Naughty Dog engine using fibers》中推广的路线TaskScheduler 正是受其启发而实现并已在商业游戏《Skyforge》PC / PS4 / X1中落地。️ TaskScheduler 架构总览线程 纤程两层模型整个调度器采用少量工作线程 大量纤程的两层结构核心文件组织如下调度器主接口Scheduler/Include/MTScheduler.h纤程上下文任务载体Scheduler/Include/MTFiberContext.h工作线程上下文Scheduler/Include/MTThreadContext.h纤程平台实现Scheduler/Include/Platform/Windows/与Scheduler/Include/Platform/Posix/每个工作线程内部包含一个特殊的scheduler fiber调度纤程也叫空闲纤程和一个私有任务队列定义见Scheduler/Include/MTThreadContext.h第 76~102 行的ThreadContext结构schedulerFiber、queue、hasNewTasksEvent三个成员各司其职调度纤程负责从任务队列取任务、在普通纤程之间来回切换任务队列是每线程私有的无锁队列空闲线程可以从别的线程偷任务work-stealing普通纤程执行具体任务遇到等待点就主动切回调度纤程。⚡ 纤程上下文切换原理切换到底做了什么上下文切换的本质是保存当前纤程的寄存器现场主要是栈指针 SP 和指令指针 RIP/EIP恢复目标纤程的现场然后跳到目标纤程的断点继续执行。TaskScheduler 在两个平台上的实现思路略有不同Windows直接操作 CPU 寄存器上下文实现位于Scheduler/Include/Platform/Windows/MTFiberOptimized.h。作者没有使用 Windows 自带的 Fiber API源码注释第 38 行附近给出的理由很直白系统纤程每个都要预留1MB 虚拟内存太浪费了。SwitchTo函数第 190~217 行的完整流程只有四步执行一次硬件内存屏障保证切换前的内存写操作对其他核心可见GetThreadContext把当前 CPU 所有寄存器保存进源纤程的上下文结构临时改写 TEB 中的栈边界StackBase/StackLimit这样目标纤程首次使用大栈时操作系统的__chkstk能正确按需提交栈页——这是纤程小栈能动态增长的关键 trickSetThreadContext恢复目标纤程的寄存器CPU 立刻在目标纤程的断点处继续跑。Linux / macOS基于 ucontextPosix 平台实现位于Scheduler/Include/Platform/Posix/MTFiber.h。Create第 145~167 行用getcontextmakecontext初始化纤程上下文并绑定一块独立分配的栈SwitchTo第 176~187 行则是一次swapcontext调用完成双向切换——保存当前、恢复目标与 Windows 版的语义完全一致。这种平台抽象统一Fiber类接口 两套底层实现正是跨平台任务调度器的核心价值上层调度逻辑一份代码三端通用。 协程式等待Yield 与 RunSubtasksAndYield纤程切换的调用逻辑在Scheduler/Source/MTFiberContext.cpp中Yield()第 74~93 行任务主动让出执行权。把任务状态置为YIELDED然后Fiber::SwitchTo(fiber, schedulerFiber)切回调度纤程调度纤程随即挑选下一个任务执行RunSubtasksAndYieldImpl第 95~127 行父任务把子任务批量分发进各线程的任务队列后将自身状态标记为AWAITING_CHILD并切换出去。当最后一个子任务完成时引用计数归零父纤程被自动恢复——这就是无需互斥锁的父子同步。纤程的完整生命周期状态定义在Scheduler/Include/MTFiberContext.h的FiberTaskStatus枚举第 45~52 行RUNNED→YIELDED/AWAITING_CHILD→FINISHED状态机清晰、易于调试。 32KB 小栈设计纤程的另一个杀手锏线程默认 1MB 栈意味着创建 256 个线程就要吃掉 256MB 虚拟内存而 TaskScheduler 在Scheduler/Include/MTScheduler.h第 177~184 行中定义了两档纤程栈规格规格栈大小每线程数量上限适用场景STANDARD32KB256 个绝大多数轻量任务EXTENDED1MB8 个深递归等吃栈任务任务在声明时通过MT_DECLARE_TASK宏声明栈需求枚举见Scheduler/Include/MTStackRequirements.h调度器会自动把任务分派到栈空间足够的纤程池上执行。256 个 32KB 纤程总共只占 8MB 栈内存这在线程模型下是完全不可想象的。 快速上手三步跑起纤程任务调度器获取源码git clone https://gitcode.com/gh_mirrors/tasksch/TaskScheduler最小使用示例更多示例见SchedulerTests/Tests/测试目录纤程专项测试在SchedulerTests/Tests/FiberTests.cpp#include MTScheduler.h struct SimpleTask { MT_DECLARE_TASK(SimpleTask, MT::StackRequirements::STANDARD, MT::TaskPriority::NORMAL, MT::Color::Blue); void Do(MT::FiberContext ctx) { // 在这里干活等待子任务用 ctx.RunSubtasksAndYield(...) } }; int main() { MT::TaskScheduler scheduler; // 1. 创建调度器 static SimpleTask tasks[1000]; scheduler.RunAsync(MT::TaskGroup::Default(), tasks, 1000); // 2. 派发 1000 个纤程任务 scheduler.WaitAll(1000); // 3. 等待并帮忙执行剩余任务 return 0; }对开发者来说API 层面完全感觉不到纤程的存在——派发任务、声明等待调度器自动完成切换与恢复。 小结为什么是纤程切换便宜用户态寄存器交换比线程内核切换快一个数量级单帧内可完成数万次调度内存省32KB 小栈 纤程池复用数百个并发执行流只占几 MB 内存无锁同步RunSubtasksAndYield用挂起—自动恢复替代互斥锁父子任务天然同步协作式可控切换时机由引擎代码决定调度行为确定、易调优。线程负责跨核心并行纤程负责线程内的海量任务编排两者结合正是现代游戏引擎多线程的标准答案。想深入细节不妨直接阅读Scheduler/Include/MTFiberContext.h与Scheduler/Source/MTFiberContext.cpp代码量不大却是理解纤程调度最干净的范本 ✨【免费下载链接】TaskSchedulerCross-platform, fiber-based, multi-threaded task scheduler designed for video games.项目地址: https://gitcode.com/gh_mirrors/tasksch/TaskScheduler创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表