5-9.【性能优化】并发任务过多时会发生什么?如何控制任务优先级?是否存在任务调度策略?

1 阅读3分钟

在 ArkTS 的并发模型中,任务的执行受到 系统调度器(Scheduler) 的统一管理。当并发任务过多时,系统并不会崩溃,但会进入一种“资源争夺”状态,这直接影响到应用的流畅度。


1. 并发任务过多时会发生什么?

当你在短时间内向 TaskPool 丢入数百个耗时任务,或者启动了大量 Worker 时,会发生以下现象:

  • 线程池饱和(Pool Saturation): TaskPool 的底层线程数是根据 CPU 核心数动态调整的。任务过多时,新任务会进入等待队列(Waiting Queue)
  • 内存压力(Memory Pressure): 每个任务在执行时都需要分配闭包、局部变量和参数副本。过多的并发会导致堆内存迅速上涨,触发频繁的 Minor GC,甚至因为 OOM(内存溢出)导致应用闪退。
  • 上下文切换开销(Context Switch): 尽管系统在优化,但过多的线程竞争 CPU 时间片会导致内核频繁切换上下文,原本 10ms 能跑完的任务可能因为“排队”变成 50ms。
  • UI 响应延迟: 虽然任务在后台跑,但如果后台任务频繁进行大数据量的跨线程通信(序列化/反序列化),会占用主线程的 CPU 时间来接收数据,导致界面卡顿。

2. 如何控制任务优先级?

ArkTS 的 TaskPool 原生支持 优先级调度。你可以在执行任务时显式指定其重要程度:

  • Priority.HIGH(高优先级): 适用于用户能感知到的、急需结果的任务(如:点击列表项后立即解析详情数据)。系统会优先从队列中取出并执行。
  • Priority.MEDIUM(默认): 适用于一般的后台预加载、非紧急逻辑。
  • Priority.LOW(低优先级): 适用于日志上传、统计数据处理等不影响当前业务流畅度的操作。

代码示例:

TypeScript

let task = new taskpool.Task(longRunningFunction, params);
// 指定高优先级执行
taskpool.execute(task, taskpool.Priority.HIGH).then((res) => {
  // 处理结果
});

3. 是否存在任务调度策略?

是的,ArkTS 的调度策略是**“智能感知”与“权重分配”**的结合:

A. 负载均衡策略

TaskPool 会监控当前 CPU 的负载。如果 CPU 已经过载,调度器会减缓新线程的创建速度,优先消耗队列中的旧任务。

B. 抢占式 vs 协作式

  • 协作式(Cooperative): ArkTS 的任务本身不可被中途强制“掐断”。一旦任务开始在线程中运行,它会一直运行直到 return
  • 取消机制: 虽然不能强制中止,但开发者可以调用 taskpool.cancel(task)。如果任务还在队列中,它将被移除;如果已经在执行,你需要通过内部的 TaskPool.getTaskInfo().isCanceled() 来手动检查并退出循环。

C. 资源隔离策略 (QoS)

系统会根据应用的运行状态(前台或后台)自动调整整个进程的 QoS (Quality of Service) 级别。前台应用的并发任务会分配到性能核心(Big Cores),而后台任务则可能被限制在能效核心(Small Cores)。


4. 并发治理的最佳实践

为了避免“任务洪水”,架构设计上应采取以下措施:

  1. 任务分片(Task Splitting): 不要丢一个运行 2 秒的巨型任务,而是拆成 20 个运行 100ms 的小任务,这样调度器能更灵活地插空执行。
  2. 生产者-消费者模型: 使用队列控制并发速率。比如限制同时进行的图片下载任务不超过 3 个。
  3. 使用 Sendable 对象: 在 HarmonyOS 最新版本中,利用 Sendable 协议实现引用传递(而非序列化拷贝),极大降低大数据跨线程传输的内存和 CPU 损耗。

总结

  • 并发多:会排队,耗内存,可能拖慢 UI。
  • 定级:重要的任务用 Priority.HIGH
  • 调优:大任务拆小,利用 Sendable 减负。