重构 ltask 的任务调度器

💡 原文中文,约3200字,阅读约需8分钟。
📝

内容提要

ltask和skynet是用于管理多个线程的工具,但应用场景不同。ltask用于游戏客户端,优化目标是低延迟;skynet用于服务器,优化目标是高负载。ltask是Ant engine的基础设施之一,通过重构调度器提升性能。ltask支持复杂的调度器算法以降低绝对帧时间,还能提高任务处理能力。最终,ltask成功优化了性能,删除了独占线程服务特性。

🔎

延伸解读

客户端与服务器调度目标的本质差异

ltask 用于游戏客户端,优化目标是低延迟,而 skynet 用于服务器,优化目标是高负载。低延迟要求调度器尽可能减少主线任务的等待时间,因此需要更复杂的算法和额外开销;高负载则追求简化调度器,充分利用所有 CPU 核心。这种对立意味着不能简单照搬服务器端的调度策略到客户端,反之亦然。

独占线程服务的三个核心问题

文章指出独占线程服务主要解决三类问题:一是服务调用会阻塞的系统 API,如 timer 的 sleep 或 IO 的 select;二是某些 C 库 API 要求在同一线程调用,甚至必须在主线程;三是像 iOS 这样窗口创建后线程会阻塞在系统调用直到退出。这些问题曾促使 ltask 区分共享工作线程和独占系统线程。

绑定工作线程并非降低延迟的万能解

重构后 ltask 支持将服务绑定在特定工作线程上,但作者强调绑定线程或物理核不等于低延迟。通过思想实验说明:若关键任务绑定到唯一工作者,当它等待依赖时无法立即恢复,反而可能拖长完成时间或浪费产能。因此,针对具体场景调优才是自研调度器的优势。

重构成果:简化代码并达成优化目标

最终 ltask 删除了独占线程服务特性,仅保留为共享服务增加绑定工作线程的能力。新调度器在游戏上工作良好,profile 结果显示达到了优化目标。代码大幅简化,C 代码减少 600 多行,Lua 代码也大量精简,说明解决核心问题后,不必要的复杂性可以被移除。

❓

Q&A

ltask和skynet的主要区别是什么?

ltask用于游戏客户端,优化目标是低延迟;而skynet用于服务器,优化目标是高负载。

重构ltask调度器的主要目的是什么?

重构ltask调度器的主要目的是提升性能,支持复杂的调度算法以降低绝对帧时间。

在游戏客户端中,如何降低绝对帧时间?

需要让主线任务有最低的延迟,确保依赖的任务准备好后可以最快时间开始运行。

独占线程服务的设计有什么问题?

独占线程服务的设计让开发者可以实现特殊的独占线程服务,但最终被删除以简化代码。

重构后的ltask调度器有哪些新特性?

重构后的ltask调度器支持将服务绑定在特定工作线程上,优化了任务调度。

为什么绑定特定工作线程未必是降低延迟的最佳方案?

因为绑定特定工作线程可能导致任务等待,影响整体完成时间,最好有替补可以处理任务。

🏷️

标签

➡️

继续阅读