【免费下载链接】Launcher
This fork of Prism Launcher adds integrated support for Ely.by accounts.
在 Ely Prism Launcher(Prism Launcher 的 Ely.by 账号整合分支)中,新建实例下载游戏文件、更新模组、检查资源更新等操作都依赖一套统一的任务进度体系。本文拆解其中的核心类SequentialTask:它如何把多个子任务串成一条流水线,并把"第 X 步 / 共 N 步"实时反映为界面上的总进度条,帮你快速读懂源码、扩展自己的多阶段任务。
一图看懂任务体系:Task → ConcurrentTask → SequentialTask
整个进度体系建立在三层继承关系上(源码位于 launcher/tasks/):
| 层级 | 类 | 职责 |
|---|---|---|
| 基类 | Task | 定义状态机(Inactive/Running/Succeeded/Failed)、进度信号progress、状态文本status |
| 中间层 | ConcurrentTask | 维护子任务队列,按max_concurrent并发调度 |
| 特化层 | SequentialTask | 将并发数固定为1,实现严格顺序执行 |
基类Task通过 Qt 信号把进度广播出去,任何界面只需connect几个信号就能画出进度条,见 launcher/tasks/Task.h:
progress(current, total):总进度数值,驱动进度条status / details:状态与详情文本stepProgress(TaskStepProgress):每个子任务的独立进度,驱动"子任务进度列表"succeeded / failed / aborted:终态通知
SequentialTask 如何实现"一个一个来"
SequentialTask的实现极其巧妙——它并没有重写调度逻辑,而是直接复用ConcurrentTask,把并发上限设为 1(launcher/tasks/SequentialTask.cpp):
SequentialTask::SequentialTask(QString task_name) : ConcurrentTask(task_name, 1) {}父类ConcurrentTask内部用三个容器管理子任务:m_queue(排队)、m_doing(执行中)、m_done(已完成)。核心调度方法executeNextSubTask在"执行中数量达到上限"时直接返回,上限为 1 时自然形成串行队列;前一个子任务完成后会再次触发调度,取出下一个(launcher/tasks/ConcurrentTask.cpp)。
总进度条的推进规则:按"完成的阶段数"计算
这是总进度条最关键的一点。SequentialTask重写了updateState()(launcher/tasks/SequentialTask.cpp):
setProgress(m_done.count(), totalSize()); setStatus(tr("Executing task %1 out of %2")...);也就是说,总进度 =已完成子任务数 / 子任务总数。每完成一个阶段,进度条前进一格,状态文本显示"Executing task 2 out of 5"。父类的updateState还有单任务特判:当只有一个子任务时,直接把该子任务的字节级进度透传给父进度条,避免"假进度"(launcher/tasks/ConcurrentTask.cpp)。
快速失败:任何一步出错,整条链立刻终止
SequentialTask覆写了subTaskFailed:子任务一失败就立即emitFailed(msg),不再执行后续阶段(launcher/tasks/SequentialTask.cpp)。这符合"后一步依赖前一步结果"的语义——比如资源没下载完,就不能做下一步的校验。
实战案例:新建实例时游戏文件的总进度条
在创建 Minecraft 实例并下载游戏文件时,InstanceCreationTask构造了一个典型的SequentialTask(launcher/InstanceCreationTask.cpp):
addTask(MinecraftLoadAndCheck):先加载并校验版本清单- 再依次
addTask四个更新阶段,来自 MinecraftInstance::createUpdateTask:FoldersTask:创建实例目录LibrariesTask:下载 Java 库LegacyFMLLibrariesTask:处理老版 Forge 库AssetUpdateTask:更新游戏资源
- 调用
propagateFromOther(task.get()),把子任务的 status/progress/stepProgress 全部桥接到外层任务,这样上层ProgressDialog无需感知内部结构
用户看到的就是:顶部总进度条按 5 个阶段推进,状态栏提示当前执行到第几步。
另一个直接继承SequentialTask的自定义任务见 launcher/minecraft/mod/tasks/GetModDependenciesTask.h,它按"先查项目信息、再取依赖清单"的顺序依次拉取 Modrinth / Flame 平台的模组元数据。
进度弹窗如何渲染这套信号
界面端由 launcher/ui/dialogs/ProgressDialog.cpp 负责:
execWithTask统一连接progress、status、stepProgress等信号- 根据
task->isMultiStep()决定是否显示子任务进度滚动区(launcher/ui/dialogs/ProgressDialog.cpp) changeStepProgress为每个子任务动态创建一个SubTaskProgressBar小进度条,子任务完成后自动隐藏(launcher/ui/dialogs/ProgressDialog.cpp),小进度条组件定义在 launcher/ui/widgets/SubTaskProgressBar.h
于是总进度条(globalProgressBar)与逐条子进度条形成了"宏观 + 微观"的双层反馈。
与 ConcurrentTask、MultipleOptionsTask 的选型对比
| 任务容器 | 并发度 | 失败行为 | 适用场景 |
|---|---|---|---|
ConcurrentTask | 默认 6 路并发 | 汇总所有失败原因 | 独立文件并行下载 |
SequentialTask | 1(严格串行) | 首个失败即终止 | 有依赖关系的阶段链 |
MultipleOptionsTask | 1(串行) | 继续尝试下一个,直到成功 | 多候选源依次兜底,见 launcher/tasks/MultipleOptionsTask.h |
单元测试test_basicSequentialRun验证了三个子任务按序全部成功的路径(tests/Task_test.cpp),可作为扩展新任务时的回归参照。
源码文件速查 📚
- 基类与进度信号:launcher/tasks/Task.h
- 顺序执行特化:launcher/tasks/SequentialTask.h
- 并发调度与状态更新:launcher/tasks/ConcurrentTask.cpp
- 实例创建串接更新任务:launcher/InstanceCreationTask.cpp
- 更新阶段清单:launcher/minecraft/MinecraftInstance.cpp
- 进度弹窗渲染:launcher/ui/dialogs/ProgressDialog.cpp
理解这套体系后你会发现:新增一个多阶段任务只需继承SequentialTask,按依赖顺序addTask,再交给ProgressDialog展示——总进度条、状态文本、子任务明细全部自动完成。
【免费下载链接】Launcher
This fork of Prism Launcher adds integrated support for Ely.by accounts.
相关推荐
Ely Prism Launcher 并发下载调度终极指南:ConcurrentTask 的进度聚合与任务编排
Ely Prism Launcher 并发下载调度终极指南:ConcurrentTask 的进度聚合与任务编排 Ely Prism Launcher(Prism
如何快速看懂 Ely Prism Launcher 的任务框架:Task、SequentialTask 与 ConcurrentTask 设计剖析
如何快速看懂 Ely Prism Launcher 的任务框架:Task、SequentialTask 与 ConcurrentTask 设计剖析 Ely Pr
Tkinter-Designer进度条:实现任务进度可视化
Tkinter Designer进度条:实现任务进度可视化 你是否在使用Tkinter开发GUI应用时,遇到过任务执行过程中界面无响应、用户不知道当前进度的问题
开发工具代码生成低代码
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考