1# TaskPool简介
2
3任务池(TaskPool)作用是为应用程序提供一个多线程的运行环境,降低整体资源的消耗、提高系统的整体性能,且您无需关心线程实例的生命周期。具体接口信息及使用方法详情请见[TaskPool](../reference/apis-arkts/js-apis-taskpool.md)。
4
5## TaskPool运作机制
6
7TaskPool运作机制示意图
8
9![zh-cn_image_0000001964858368](figures/zh-cn_image_0000001964858368.png)
10
11TaskPool支持开发者在宿主线程封装任务抛给任务队列,系统选择合适的工作线程,进行任务的分发及执行,再将结果返回给宿主线程。接口直观易用,支持任务的执行、取消,以及指定优先级的能力,同时通过系统统一线程管理,结合动态调度及负载均衡算法,可以节约系统资源。系统默认会启动一个任务工作线程,当任务较多时会扩容,工作线程数量上限跟当前设备的物理核数相关,具体数量内部管理,保证最优的调度及执行效率,长时间没有任务分发时会缩容,减少工作线程数量。
12
13## TaskPool注意事项
14
15- 实现任务的函数需要使用[@Concurrent装饰器](#concurrent装饰器)标注,且仅支持在.ets文件中使用。
16
17- 从API version 11开始,跨并发实例传递带方法的实例对象时,该类必须使用装饰器[@Sendable装饰器](arkts-sendable.md#sendable装饰器)标注,且仅支持在.ets文件中使用。
18
19- 任务函数在TaskPool工作线程的执行耗时不能超过3分钟(不包含Promise和async/await异步调用的耗时,例如网络下载、文件读写等I/O任务的耗时),否则会被强制退出。
20
21- 实现任务的函数入参需满足序列化支持的类型,详情请参见[线程间通信对象](interthread-communication-overview.md)。
22
23- ArrayBuffer参数在TaskPool中默认转移,需要设置转移列表的话可通过接口[setTransferList()](../reference/apis-arkts/js-apis-taskpool.md#settransferlist10)设置。
24
25- 由于不同线程中上下文对象是不同的,因此TaskPool工作线程只能使用线程安全的库,例如UI相关的非线程安全库不能使用。
26
27- 序列化传输的数据量大小限制为16MB。
28
29- [Priority](../reference/apis-arkts/js-apis-taskpool.md#priority)的IDLE优先级是用来标记需要在后台运行的耗时任务(例如数据同步、备份),它的优先级别是最低的。这种优先级标记的任务只会在所有线程都空闲的情况下触发执行,并且只会占用一个线程来执行。
30
31- Promise不支持跨线程传递,如果TaskPool返回pending或rejected状态的Promise,会返回失败;对于fulfilled状态的Promise,TaskPool会解析返回的结果,如果结果可以跨线程传递,则返回成功。
32
33- 不支持在TaskPool工作线程中使用[AppStorage](../quick-start/arkts-appstorage.md)。
34
35## \@Concurrent装饰器
36
37在使用[TaskPool](../reference/apis-arkts/js-apis-taskpool.md)时,执行的并发函数需要使用该装饰器修饰,否则无法通过相关校验。
38
39> **说明:**
40>
41> 从API version 9开始,支持使用\@Concurrent装饰器声明并校验并发函数。
42
43### 装饰器说明
44
45| \@Concurrent并发装饰器 | 说明 |
46| -------- | -------- |
47| 装饰器参数 | 无。 |
48| 使用场景 | 仅支持在Stage模型的工程中使用。仅支持在.ets文件中使用。 |
49| 装饰的函数类型 | 允许标注async函数或普通函数。禁止标注generator、箭头函数、类方法。不支持类成员函数或者匿名函数。 |
50| 装饰的函数内的变量类型 | 允许使用local变量、入参和通过import引入的变量。禁止使用闭包变量。 |
51| 装饰的函数内的返回值类型 | 支持的类型请查[线程间通信对象](interthread-communication-overview.md)。 |
52
53> **说明:**
54>
55> 由于\@Concurrent标记的函数不能访问闭包,因此\@Concurrent标记的函数内部不能调用当前文件的其他函数,例如:
56>
57> ```ts
58> function bar() {
59> }
60>
61> @Concurrent
62> function foo() {
63> bar(); // 违反闭包原则,报错
64> }
65> ```
66
67### 装饰器使用示例
68
69#### 并发函数一般使用
70
71并发函数为一个计算两数之和的普通函数,taskpool执行该函数并返回结果。
72
73示例:
74
75```ts
76import { taskpool } from '@kit.ArkTS';
77
78@Concurrent
79function add(num1: number, num2: number): number {
80  return num1 + num2;
81}
82
83async function ConcurrentFunc(): Promise<void> {
84  try {
85    let task: taskpool.Task = new taskpool.Task(add, 1, 2);
86    console.info("taskpool res is: " + await taskpool.execute(task));
87  } catch (e) {
88    console.error("taskpool execute error is: " + e);
89  }
90}
91
92@Entry
93@Component
94struct Index {
95  @State message: string = 'Hello World'
96
97  build() {
98    Row() {
99      Column() {
100        Text(this.message)
101          .fontSize(50)
102          .fontWeight(FontWeight.Bold)
103          .onClick(() => {
104            ConcurrentFunc();
105          })
106      }
107      .width('100%')
108    }
109    .height('100%')
110  }
111}
112```
113
114#### 并发函数返回Promise
115
116并发函数中返回Promise的表现需关注,如下例所示,其中testPromise、testPromise1等并发同步函数会处理该Promise并返回结果。
117
118示例:
119
120```ts
121import { taskpool } from '@kit.ArkTS';
122
123@Concurrent
124function testPromise(args1: number, args2: number): Promise<number> {
125  return new Promise<number>((testFuncA, testFuncB)=>{
126    testFuncA(args1 + args2);
127  });
128}
129
130@Concurrent
131async function testPromise1(args1: number, args2: number): Promise<number> {
132  return new Promise<number>((testFuncA, testFuncB)=>{
133    testFuncA(args1 + args2);
134  });
135}
136
137@Concurrent
138async function testPromise2(args1: number, args2: number): Promise<number> {
139  return await new Promise<number>((testFuncA, testFuncB)=>{
140    testFuncA(args1 + args2);
141  });
142}
143
144@Concurrent
145function testPromise3() {
146  return Promise.resolve(1);
147}
148
149@Concurrent
150async function testPromise4(): Promise<number> {
151  return 1;
152}
153
154@Concurrent
155async function testPromise5(): Promise<string> {
156  return await new Promise((resolve) => {
157    setTimeout(()=>{
158      resolve("Promise setTimeout after resolve");
159    }, 1000)
160  });
161}
162
163async function testConcurrentFunc() {
164  let task1: taskpool.Task = new taskpool.Task(testPromise, 1, 2);
165  let task2: taskpool.Task = new taskpool.Task(testPromise1, 1, 2);
166  let task3: taskpool.Task = new taskpool.Task(testPromise2, 1, 2);
167  let task4: taskpool.Task = new taskpool.Task(testPromise3);
168  let task5: taskpool.Task = new taskpool.Task(testPromise4);
169  let task6: taskpool.Task = new taskpool.Task(testPromise5);
170
171  taskpool.execute(task1).then((d:object)=>{
172    console.info("task1 res is: " + d);
173  }).catch((e:object)=>{
174    console.info("task1 catch e: " + e);
175  })
176  taskpool.execute(task2).then((d:object)=>{
177    console.info("task2 res is: " + d);
178  }).catch((e:object)=>{
179    console.info("task2 catch e: " + e);
180  })
181  taskpool.execute(task3).then((d:object)=>{
182    console.info("task3 res is: " + d);
183  }).catch((e:object)=>{
184    console.info("task3 catch e: " + e);
185  })
186  taskpool.execute(task4).then((d:object)=>{
187    console.info("task4 res is: " + d);
188  }).catch((e:object)=>{
189    console.info("task4 catch e: " + e);
190  })
191  taskpool.execute(task5).then((d:object)=>{
192    console.info("task5 res is: " + d);
193  }).catch((e:object)=>{
194    console.info("task5 catch e: " + e);
195  })
196  taskpool.execute(task6).then((d:object)=>{
197    console.info("task6 res is: " + d);
198  }).catch((e:object)=>{
199    console.info("task6 catch e: " + e);
200  })
201}
202
203@Entry
204@Component
205struct Index {
206  @State message: string = 'Hello World';
207
208  build() {
209    Row() {
210      Column() {
211        Button(this.message)
212          .fontSize(50)
213          .fontWeight(FontWeight.Bold)
214          .onClick(() => {
215            testConcurrentFunc();
216          })
217      }
218      .width('100%')
219    }
220    .height('100%')
221  }
222}
223```
224
225#### 并发函数中使用自定义类或函数
226
227并发函数中使用自定义类或函数时需定义在不同文件,否则会被认为是闭包,如下例所示。
228
229示例:
230
231```ts
232// Index.ets
233import { taskpool } from '@kit.ArkTS';
234import { BusinessError } from '@kit.BasicServicesKit';
235import { testAdd, MyTestA, MyTestB } from './Test';
236
237function add(arg: number) {
238  return ++arg;
239}
240
241class TestA {
242  constructor(name: string) {
243    this.name = name;
244  }
245  name: string = 'ClassA';
246}
247
248class TestB {
249  static nameStr: string = 'ClassB';
250}
251
252@Concurrent
253function TestFunc() {
254  // case1:在并发函数中直接调用同文件内定义的类或函数
255
256  // 直接调用同文件定义的函数add(),add飘红报错:Only imported variables and local variables can be used in @Concurrent decorated functions. <ArkTSCheck>
257  // add(1);
258  // 直接使用同文件定义的TestA构造,TestA飘红报错:Only imported variables and local variables can be used in @Concurrent decorated functions. <ArkTSCheck>
259  // let a = new TestA("aaa");
260  // 直接访问同文件定义的TestB的成员nameStr,TestB飘红报错:Only imported variables and local variables can be used in @Concurrent decorated functions. <ArkTSCheck>
261  // console.info("TestB name is: " + TestB.nameStr);
262
263  // case2:在并发函数中调用定义在Test.ets文件并导入当前文件的类或函数
264
265  // 输出结果:res1 is: 2
266  console.info("res1 is: " + testAdd(1));
267  let tmpStr = new MyTestA("TEST A");
268  // 输出结果:res2 is: TEST A
269  console.info("res2 is: " + tmpStr.name);
270  // 输出结果:res3 is: MyTestB
271  console.info("res3 is: " + MyTestB.nameStr);
272}
273
274
275@Entry
276@Component
277struct Index {
278  @State message: string = 'Hello World';
279
280  build() {
281    RelativeContainer() {
282      Text(this.message)
283        .id('HelloWorld')
284        .fontSize(50)
285        .fontWeight(FontWeight.Bold)
286        .alignRules({
287          center: { anchor: '__container__', align: VerticalAlign.Center },
288          middle: { anchor: '__container__', align: HorizontalAlign.Center }
289        })
290        .onClick(() => {
291          let task = new taskpool.Task(TestFunc);
292          taskpool.execute(task).then(() => {
293            console.info("taskpool: execute task success!");
294          }).catch((e:BusinessError) => {
295            console.error(`taskpool: execute: Code: ${e.code}, message: ${e.message}`);
296          })
297        })
298    }
299    .height('100%')
300    .width('100%')
301  }
302}
303```
304
305```ts
306// Test.ets
307export function testAdd(arg: number) {
308  return ++arg;
309}
310
311@Sendable
312export class MyTestA {
313  constructor(name: string) {
314    this.name = name;
315  }
316  name: string = 'MyTestA';
317}
318
319export class MyTestB {
320  static nameStr:string = 'MyTestB';
321}
322```
323
324#### 并发异步函数中使用Promise
325
326并发异步函数中如果使用Promise,建议搭配await使用捕获Promise中可能发生的异常。推荐使用示例如下。
327
328示例:
329
330```ts
331import { taskpool } from '@kit.ArkTS'
332
333@Concurrent
334async function testPromiseError() {
335  await new Promise<number>((resolve, reject) => {
336    resolve(1);
337  }).then(()=>{
338    throw new Error("testPromise Error");
339  })
340}
341
342@Concurrent
343async function testPromiseError1() {
344  await new Promise<string>((resolve, reject) => {
345    reject("testPromiseError1 Error msg");
346  })
347}
348
349@Concurrent
350function testPromiseError2() {
351  return new Promise<string>((resolve, reject) => {
352    reject("testPromiseError2 Error msg");
353  })
354}
355
356async function testConcurrentFunc() {
357  let task1: taskpool.Task = new taskpool.Task(testPromiseError);
358  let task2: taskpool.Task = new taskpool.Task(testPromiseError1);
359  let task3: taskpool.Task = new taskpool.Task(testPromiseError2);
360
361  taskpool.execute(task1).then((d:object)=>{
362    console.info("task1 res is: " + d);
363  }).catch((e:object)=>{
364    console.info("task1 catch e: " + e);
365  })
366  taskpool.execute(task2).then((d:object)=>{
367    console.info("task2 res is: " + d);
368  }).catch((e:object)=>{
369    console.info("task2 catch e: " + e);
370  })
371  taskpool.execute(task3).then((d:object)=>{
372    console.info("task3 res is: " + d);
373  }).catch((e:object)=>{
374    console.info("task3 catch e: " + e);
375  })
376}
377
378@Entry
379@Component
380struct Index {
381  @State message: string = 'Hello World';
382
383  build() {
384    Row() {
385      Column() {
386        Button(this.message)
387          .fontSize(50)
388          .fontWeight(FontWeight.Bold)
389          .onClick(() => {
390            testConcurrentFunc();
391          })
392      }
393      .width('100%')
394    }
395    .height('100%')
396  }
397}
398```
399