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

如何在Dart命令行程序中实现堆叠式并行进度条

解决Dart Isolate并行进度条堆叠更新问题

在Dart编译农场场景中,10个Isolate并发处理250个固件构建任务,每个Isolate串行打印无换行进度条时会互相覆盖。要实现进度条堆叠显示且可单独更新,同时不使用清屏命令,可通过ANSI终端控制序列+固定行号映射来实现:

核心思路

  1. 给每个构建任务分配固定的终端行位置,初始时先打印所有任务的占位进度条,记录每个任务对应的行号。
  2. 使用ANSI光标移动命令定位到目标行,清除该行原有内容后打印更新后的进度条,避免干扰其他行的内容。

实现代码

定义ANSI终端控制码

// ANSI终端控制序列:清除当前行(从光标到行尾)
const ansiClearLine = '\x1B[K';
// 光标向上移动n行的模板,替换%d为行数
const ansiCursorUp = '\x1B[%dA';
// 光标向下移动n行的模板,替换%d为行数
const ansiCursorDown = '\x1B[%dB';

初始化进度条行映射

在主Isolate中预先创建所有任务的占位行,并记录每个任务对应的行号(如果任务是动态创建的,可在任务启动时执行此步骤):

// 存储任务ID到终端行号的映射,需保证线程安全,可配合Mutex使用
final Map<String, int> taskLineMap = {};
int currentLine = 0;

// 初始化所有任务的占位进度条
void initTaskProgressBars(List<String> allTaskIds, String module) {
  for (final taskId in allTaskIds) {
    // 打印初始进度条(0%)
    stdout.write('$module: task ID $taskId: ${''.padLeft(40, '•')} 0%\n');
    // 记录当前任务对应的行号
    taskLineMap[taskId] = currentLine++;
  }
  // 光标回到所有进度条的上方,准备后续更新
  stdout.write(ansiCursorUp.replaceFirst('%d', currentLine.toString()));
}

单个任务的进度更新逻辑

在每个Isolate的任务处理函数中,调用此方法更新对应任务的进度条:

void updateTaskProgress(String module, String taskId, int progress) {
  const progressBarLength = 40;
  final completedLength = (progress * progressBarLength / 100).floor();
  final remainingLength = progressBarLength - completedLength;

  // 获取当前任务对应的终端行号
  final targetLine = taskLineMap[taskId]!;

  // 移动光标到目标行
  stdout.write(ansiCursorDown.replaceFirst('%d', targetLine.toString()));
  // 清除当前行原有内容
  stdout.write(ansiClearLine);
  
  if (progress == 100) {
    // 任务完成时打印完成状态
    stdout.write('$module: task ID $taskId: 🟢 Completed\n');
  } else {
    // 打印更新后的进度条
    stdout.write(
      '$module: task ID $taskId: ${''.padLeft(completedLength, '=')}${''.padLeft(remainingLength, '•')} ${progress.toStringAsFixed(0)}%',
    );
  }

  // 光标回到进度条区域的上方,不影响后续其他任务的更新
  stdout.write(ansiCursorUp.replaceFirst('%d', targetLine.toString()));
}

注意事项

  • 确保运行环境的终端支持ANSI控制序列:Linux/macOS终端、Windows Terminal均支持,旧版Windows命令提示符可能需要开启相关设置。
  • 行号映射的线程安全:由于多个Isolate会访问taskLineMap,需使用Mutex或Dart的跨Isolate通信机制(如IsolateNameServer)保证并发安全。
  • 动态任务场景:如果任务不是预先全部创建的,可在每个任务启动时打印占位行并记录行号,避免提前占用过多终端行。

内容的提问来源于stack exchange,提问作者Stéphane de Luca

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 15:27:38