鸿蒙 TaskPool如何多个任务并行执行
/ 实现同上...TaskPool 的特点:提升响应性:将耗时任务移出主线程,避免UI卡顿充分利用多核:自动利用设备多核CPU并行处理简化开发:无需手动管理线程,专注业务逻辑灵活任务管理:支持独立任务和任务组两种模式选择:简单任务→ 使用独立 Task复杂任务→ 使用 TaskGroup 拆分并行处理需要聚合结果→ 使用 TaskGroup 统一返回。
本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新
一、TaskPool
1. 概念
-
TaskPool 是 ArkTS 提供的多线程环境,用于在后台执行耗时任务
-
避免阻塞 UI 主线程,提升应用响应速度和用户体验
-
系统自动管理线程生命周期,使用者无需关心线程创建和销毁
2. 特性
-
自动线程管理:系统根据任务量自动扩缩容工作线程
-
任务队列:支持任务提交、执行、取消和优先级设置
-
结果返回:任务执行完成后将结果返回给宿主线程
二、执行独立的任务
1. 场景
-
单个独立的耗时计算任务
-
任务完成后需要将结果返回主线程
-
如图片加载、数据计算、文件处理等
2. 步骤
步骤1:定义数据模型
// ImageDataModel.ets
export class ImageDataItem {
imageSrc: string | Resource = '';
description: string | Resource = '';
constructor(imageSrc: string | Resource = '', description: string | Resource = '') {
this.imageSrc = imageSrc;
this.description = description;
}
}
步骤2:创建并发任务函数
// ImageLoader.ets
import { ImageDataItem } from './ImageDataModel';
// 必须使用 @Concurrent 装饰器标记
@Concurrent
export function loadImageData(itemCount: number): ImageDataItem[] {
let imageDataList: ImageDataItem[] = [];
// 模拟耗时操作:生成图片数据
for (let i = 0; i < itemCount; i++) {
const baseIndex = i * 3;
// 循环使用预定义的图片资源
imageDataList.push(new ImageDataItem('$media:homeIcon', `图片${baseIndex + 1}`));
imageDataList.push(new ImageDataItem('$media:profileIcon', `图片${baseIndex + 2}`));
imageDataList.push(new ImageDataItem('$media:settingsIcon', `图片${baseIndex + 3}`));
}
return imageDataList;
}
步骤3:在主线程中执行任务
// MainPage.ets
import { taskpool } from '@kit.ArkTS';
import { ImageDataItem } from './ImageDataModel';
import { loadImageData } from './ImageLoader';
@Entry
@Component
struct MainPage {
@State pageTitle: string = '图片加载示例';
@State imageList: ImageDataItem[] = [];
build() {
Column() {
Text(this.pageTitle)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 })
Button('开始加载图片')
.fontSize(18)
.padding(10)
.onClick(() => {
this.startImageLoading();
})
}
.padding(20)
.width('100%')
}
private startImageLoading(): void {
// 创建任务实例,传入任务函数和参数
const imageLoadTask: taskpool.Task = new taskpool.Task(loadImageData, 25);
// 执行任务并处理结果
taskpool.execute(imageLoadTask).then((result: object) => {
// 将结果转换为具体类型
this.imageList = result as ImageDataItem[];
// 输出日志验证结果
console.info(`图片数据加载完成,共 ${this.imageList.length} 项`);
console.info(`第一项描述: ${this.imageList[0]?.description}`);
}).catch((error) => {
console.error(`图片加载失败: ${error.message}`);
});
}
}
3. 说明
-
taskpool.Task:封装任务函数和参数
-
taskpool.execute():提交任务到线程池执行
-
Promise 结果:通过 then() 回调获取任务执行结果
三、执行多个耗时任务
1. 场景
-
多个任务需要并行执行
-
需要等待所有任务完成后统一处理结果
-
大数据集拆分处理
2. 步骤
步骤1:定义任务函数(复用独立任务的函数)
// ImageLoader.ets
@Concurrent
export function loadImageData(itemCount: number): ImageDataItem[] {
// 实现同上...
}
步骤2:使用 TaskGroup 管理多个任务
// MultiTaskPage.ets
import { taskpool } from '@kit.ArkTS';
import { ImageDataItem } from './ImageDataModel';
import { loadImageData } from './ImageLoader';
@Entry
@Component
struct MultiTaskPage {
@State pageTitle: string = '并行图片加载';
@State combinedImageList: ImageDataItem[] = [];
build() {
Column() {
Text(this.pageTitle)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 })
Button('开始并行加载')
.fontSize(18)
.padding(10)
.onClick(() => {
this.startParallelLoading();
})
Text(`已加载图片数: ${this.combinedImageList.length}`)
.fontSize(16)
.margin({ top: 20 })
}
.padding(20)
.width('100%')
}
private startParallelLoading(): void {
// 创建任务组
const imageTaskGroup: taskpool.TaskGroup = new taskpool.TaskGroup();
// 添加多个任务到任务组(不同参数)
imageTaskGroup.addTask(new taskpool.Task(loadImageData, 15)); // 加载75张
imageTaskGroup.addTask(new taskpool.Task(loadImageData, 10)); // 加载50张
imageTaskGroup.addTask(new taskpool.Task(loadImageData, 8)); // 加载40张
imageTaskGroup.addTask(new taskpool.Task(loadImageData, 12)); // 加载60张
// 执行任务组
taskpool.execute(imageTaskGroup).then((results: object) => {
const taskResults = results as ImageDataItem[][];
this.processTaskResults(taskResults);
}).catch((error) => {
console.error(`并行加载失败: ${error.message}`);
});
}
private processTaskResults(results: ImageDataItem[][]): void {
// 清空现有数据
this.combinedImageList = [];
// 合并所有任务的结果
for (const taskResult of results) {
this.combinedImageList.push(...taskResult);
}
// 输出统计信息
console.info(`并行加载完成,共 ${results.length} 个任务`);
console.info(`合并后总图片数: ${this.combinedImageList.length}`);
// 验证数据完整性
let totalFromTasks = 0;
results.forEach((arr, index) => {
console.info(`任务${index + 1}加载了 ${arr.length} 张图片`);
totalFromTasks += arr.length;
});
console.info(`各任务图片数总和: ${totalFromTasks}`);
}
}
3. TaskGroup 的优势
结果返回方式对比:
| 方式 | 返回时机 | 结果格式 | 适用场景 |
|---|---|---|---|
| 单个Task | 每个任务完成立即返回 | 单个结果对象 | 独立任务 |
| TaskGroup | 所有任务完成后统一返回 | 结果数组 | 需要所有结果的场景 |
性能优势:
// 大数据处理示例:将10000条数据拆分成多个任务
function processLargeDataset() {
const totalRecords = 10000;
const batchSize = 1000;
const taskGroup = new taskpool.TaskGroup();
// 拆分成10个任务并行处理
for (let i = 0; i < totalRecords / batchSize; i++) {
const startIndex = i * batchSize;
const endIndex = startIndex + batchSize;
taskGroup.addTask(new taskpool.Task(processDataBatch, startIndex, endIndex));
}
// 所有批次处理完成后合并结果
taskpool.execute(taskGroup).then((allResults) => {
const finalResult = mergeAllResults(allResults);
console.info(`处理完成,共 ${finalResult.length} 条记录`);
});
}
四、说明
1. @Concurrent 装饰器
-
必须使用:在 TaskPool 中执行的函数必须用
@Concurrent装饰 -
文件限制:仅支持在
.ets文件中使用 -
函数限制:不能访问闭包变量,只能使用局部变量和参数
2. 参数和返回值
-
序列化要求:参数和返回值必须支持序列化
-
数据类型:不支持
@State、@Prop、@Link等装饰的复杂类型 -
数据量限制:单次序列化数据量不超过 16MB
五、使用场景
| 场景特征 | 使用独立Task | 使用TaskGroup |
|---|---|---|
| 任务数量 | 单个任务 | 多个相关任务 |
| 结果依赖 | 任务间无依赖 | 需要所有任务结果 |
| 数据处理 | 小到中等数据量 | 大数据集拆分处理 |
| 执行时机 | 立即执行,单独返回 | 等待全部完成,统一返回 |
六、总结
TaskPool 的特点:
-
提升响应性:将耗时任务移出主线程,避免UI卡顿
-
充分利用多核:自动利用设备多核CPU并行处理
-
简化开发:无需手动管理线程,专注业务逻辑
-
灵活任务管理:支持独立任务和任务组两种模式
选择:
-
简单任务 → 使用独立 Task
-
复杂任务 → 使用 TaskGroup 拆分并行处理
-
需要聚合结果 → 使用 TaskGroup 统一返回
更多推荐



所有评论(0)