在 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. 并发治理的最佳实践
为了避免“任务洪水”,架构设计上应采取以下措施:
- 任务分片(Task Splitting): 不要丢一个运行 2 秒的巨型任务,而是拆成 20 个运行 100ms 的小任务,这样调度器能更灵活地插空执行。
- 生产者-消费者模型: 使用队列控制并发速率。比如限制同时进行的图片下载任务不超过 3 个。
- 使用
Sendable对象: 在 HarmonyOS 最新版本中,利用Sendable协议实现引用传递(而非序列化拷贝),极大降低大数据跨线程传输的内存和 CPU 损耗。
总结
- 并发多:会排队,耗内存,可能拖慢 UI。
- 定级:重要的任务用
Priority.HIGH。 - 调优:大任务拆小,利用
Sendable减负。