• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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- TaskPool支持开发者在宿主线程封装任务抛给任务队列,理论上可以支持任意多的任务,但任务的执行受限于任务的优先级以及系统资源的影响,在工作线程扩容到最大后,可能会导致任务的执行效率下降。
36
37- TaskPool不支持指定任务所运行的线程,任务会被分配到空闲的线程中执行。如果需要指定任务运行的线程,建议使用[Worker](./worker-introduction.md)来实现。
38
39## \@Concurrent装饰器
40
41在使用[TaskPool](../reference/apis-arkts/js-apis-taskpool.md)时,执行的并发函数需要使用该装饰器修饰,否则无法通过相关校验。
42
43> **说明:**
44>
45> 从API version 9开始,支持使用\@Concurrent装饰器声明并校验并发函数。
46
47### 装饰器说明
48
49| \@Concurrent并发装饰器 | 说明 |
50| -------- | -------- |
51| 装饰器参数 | 无。 |
52| 使用场景 | 仅支持在Stage模型的工程中使用。仅支持在.ets文件中使用。 |
53| 装饰的函数类型 | 允许标注async函数或普通函数。禁止标注generator、箭头函数、类方法。不支持类成员函数或者匿名函数。 |
54| 装饰的函数内的变量类型 | 允许使用local变量、入参和通过import引入的变量。禁止使用闭包变量。 |
55| 装饰的函数内的返回值类型 | 支持的类型请查[线程间通信对象](interthread-communication-overview.md)。 |
56
57> **说明:**
58>
59> 由于\@Concurrent标记的函数不能访问闭包,因此\@Concurrent标记的函数内部不能调用当前文件的其他函数,例如:
60>
61> ```ts
62> function bar() {
63> }
64>
65> @Concurrent
66> function foo() {
67> bar(); // 违反闭包原则,报错
68> }
69> ```
70
71### 装饰器使用示例
72
73#### 并发函数一般使用
74
75并发函数为一个计算两数之和的普通函数,taskpool执行该函数并返回结果。
76
77示例:
78
79```ts
80import { taskpool } from '@kit.ArkTS';
81
82@Concurrent
83function add(num1: number, num2: number): number {
84  return num1 + num2;
85}
86
87async function ConcurrentFunc(): Promise<void> {
88  try {
89    let task: taskpool.Task = new taskpool.Task(add, 1, 2);
90    console.info("taskpool res is: " + await taskpool.execute(task));
91  } catch (e) {
92    console.error("taskpool execute error is: " + e);
93  }
94}
95
96@Entry
97@Component
98struct Index {
99  @State message: string = 'Hello World'
100
101  build() {
102    Row() {
103      Column() {
104        Text(this.message)
105          .fontSize(50)
106          .fontWeight(FontWeight.Bold)
107          .onClick(() => {
108            ConcurrentFunc();
109          })
110      }
111      .width('100%')
112    }
113    .height('100%')
114  }
115}
116```
117
118#### 并发函数返回Promise
119
120并发函数中返回Promise的表现需关注,如下例所示,其中testPromise、testPromise1等并发同步函数会处理该Promise并返回结果。
121
122示例:
123
124```ts
125import { taskpool } from '@kit.ArkTS';
126
127@Concurrent
128function testPromise(args1: number, args2: number): Promise<number> {
129  return new Promise<number>((resolve, reject)=>{
130    resolve(args1 + args2);
131  });
132}
133
134@Concurrent
135async function testPromise1(args1: number, args2: number): Promise<number> {
136  return new Promise<number>((resolve, reject)=>{
137    resolve(args1 + args2);
138  });
139}
140
141@Concurrent
142async function testPromise2(args1: number, args2: number): Promise<number> {
143  return await new Promise<number>((resolve, reject)=>{
144    resolve(args1 + args2);
145  });
146}
147
148@Concurrent
149function testPromise3() {
150  return Promise.resolve(1);
151}
152
153@Concurrent
154async function testPromise4(): Promise<number> {
155  return 1;
156}
157
158@Concurrent
159async function testPromise5(): Promise<string> {
160  return await new Promise((resolve) => {
161    setTimeout(()=>{
162      resolve("Promise setTimeout after resolve");
163    }, 1000)
164  });
165}
166
167async function testConcurrentFunc() {
168  let task1: taskpool.Task = new taskpool.Task(testPromise, 1, 2);
169  let task2: taskpool.Task = new taskpool.Task(testPromise1, 1, 2);
170  let task3: taskpool.Task = new taskpool.Task(testPromise2, 1, 2);
171  let task4: taskpool.Task = new taskpool.Task(testPromise3);
172  let task5: taskpool.Task = new taskpool.Task(testPromise4);
173  let task6: taskpool.Task = new taskpool.Task(testPromise5);
174
175  taskpool.execute(task1).then((d:object)=>{
176    console.info("task1 res is: " + d);
177  }).catch((e:object)=>{
178    console.info("task1 catch e: " + e);
179  })
180  taskpool.execute(task2).then((d:object)=>{
181    console.info("task2 res is: " + d);
182  }).catch((e:object)=>{
183    console.info("task2 catch e: " + e);
184  })
185  taskpool.execute(task3).then((d:object)=>{
186    console.info("task3 res is: " + d);
187  }).catch((e:object)=>{
188    console.info("task3 catch e: " + e);
189  })
190  taskpool.execute(task4).then((d:object)=>{
191    console.info("task4 res is: " + d);
192  }).catch((e:object)=>{
193    console.info("task4 catch e: " + e);
194  })
195  taskpool.execute(task5).then((d:object)=>{
196    console.info("task5 res is: " + d);
197  }).catch((e:object)=>{
198    console.info("task5 catch e: " + e);
199  })
200  taskpool.execute(task6).then((d:object)=>{
201    console.info("task6 res is: " + d);
202  }).catch((e:object)=>{
203    console.info("task6 catch e: " + e);
204  })
205}
206
207@Entry
208@Component
209struct Index {
210  @State message: string = 'Hello World';
211
212  build() {
213    Row() {
214      Column() {
215        Button(this.message)
216          .fontSize(50)
217          .fontWeight(FontWeight.Bold)
218          .onClick(() => {
219            testConcurrentFunc();
220          })
221      }
222      .width('100%')
223    }
224    .height('100%')
225  }
226}
227```
228
229#### 并发函数中使用自定义类或函数
230
231并发函数中使用自定义类或函数时需定义在不同文件,否则会被认为是闭包,如下例所示。
232
233示例:
234
235```ts
236// Index.ets
237import { taskpool } from '@kit.ArkTS';
238import { BusinessError } from '@kit.BasicServicesKit';
239import { testAdd, MyTestA, MyTestB } from './Test';
240
241function add(arg: number) {
242  return ++arg;
243}
244
245class TestA {
246  constructor(name: string) {
247    this.name = name;
248  }
249  name: string = 'ClassA';
250}
251
252class TestB {
253  static nameStr: string = 'ClassB';
254}
255
256@Concurrent
257function TestFunc() {
258  // case1:在并发函数中直接调用同文件内定义的类或函数
259
260  // 直接调用同文件定义的函数add(),add飘红报错:Only imported variables and local variables can be used in @Concurrent decorated functions. <ArkTSCheck>
261  // add(1);
262  // 直接使用同文件定义的TestA构造,TestA飘红报错:Only imported variables and local variables can be used in @Concurrent decorated functions. <ArkTSCheck>
263  // let a = new TestA("aaa");
264  // 直接访问同文件定义的TestB的成员nameStr,TestB飘红报错:Only imported variables and local variables can be used in @Concurrent decorated functions. <ArkTSCheck>
265  // console.info("TestB name is: " + TestB.nameStr);
266
267  // case2:在并发函数中调用定义在Test.ets文件并导入当前文件的类或函数
268
269  // 输出结果:res1 is: 2
270  console.info("res1 is: " + testAdd(1));
271  let tmpStr = new MyTestA("TEST A");
272  // 输出结果:res2 is: TEST A
273  console.info("res2 is: " + tmpStr.name);
274  // 输出结果:res3 is: MyTestB
275  console.info("res3 is: " + MyTestB.nameStr);
276}
277
278
279@Entry
280@Component
281struct Index {
282  @State message: string = 'Hello World';
283
284  build() {
285    RelativeContainer() {
286      Text(this.message)
287        .id('HelloWorld')
288        .fontSize(50)
289        .fontWeight(FontWeight.Bold)
290        .alignRules({
291          center: { anchor: '__container__', align: VerticalAlign.Center },
292          middle: { anchor: '__container__', align: HorizontalAlign.Center }
293        })
294        .onClick(() => {
295          let task = new taskpool.Task(TestFunc);
296          taskpool.execute(task).then(() => {
297            console.info("taskpool: execute task success!");
298          }).catch((e:BusinessError) => {
299            console.error(`taskpool: execute: Code: ${e.code}, message: ${e.message}`);
300          })
301        })
302    }
303    .height('100%')
304    .width('100%')
305  }
306}
307```
308
309```ts
310// Test.ets
311export function testAdd(arg: number) {
312  return ++arg;
313}
314
315@Sendable
316export class MyTestA {
317  constructor(name: string) {
318    this.name = name;
319  }
320  name: string = 'MyTestA';
321}
322
323export class MyTestB {
324  static nameStr:string = 'MyTestB';
325}
326```
327
328#### 并发异步函数中使用Promise
329
330并发异步函数中如果使用Promise,建议搭配await使用。这样TaskPool会捕获Promise中可能发生的异常。推荐使用示例如下。
331
332示例:
333
334```ts
335import { taskpool } from '@kit.ArkTS'
336
337@Concurrent
338async function testPromiseError() {
339  await new Promise<number>((resolve, reject) => {
340    resolve(1);
341  }).then(()=>{
342    throw new Error("testPromise Error");
343  })
344}
345
346@Concurrent
347async function testPromiseError1() {
348  await new Promise<string>((resolve, reject) => {
349    reject("testPromiseError1 Error msg");
350  })
351}
352
353@Concurrent
354function testPromiseError2() {
355  return new Promise<string>((resolve, reject) => {
356    reject("testPromiseError2 Error msg");
357  })
358}
359
360async function testConcurrentFunc() {
361  let task1: taskpool.Task = new taskpool.Task(testPromiseError);
362  let task2: taskpool.Task = new taskpool.Task(testPromiseError1);
363  let task3: taskpool.Task = new taskpool.Task(testPromiseError2);
364
365  taskpool.execute(task1).then((d:object)=>{
366    console.info("task1 res is: " + d);
367  }).catch((e:object)=>{
368    console.info("task1 catch e: " + e);
369  })
370  taskpool.execute(task2).then((d:object)=>{
371    console.info("task2 res is: " + d);
372  }).catch((e:object)=>{
373    console.info("task2 catch e: " + e);
374  })
375  taskpool.execute(task3).then((d:object)=>{
376    console.info("task3 res is: " + d);
377  }).catch((e:object)=>{
378    console.info("task3 catch e: " + e);
379  })
380}
381
382@Entry
383@Component
384struct Index {
385  @State message: string = 'Hello World';
386
387  build() {
388    Row() {
389      Column() {
390        Button(this.message)
391          .fontSize(50)
392          .fontWeight(FontWeight.Bold)
393          .onClick(() => {
394            testConcurrentFunc();
395          })
396      }
397      .width('100%')
398    }
399    .height('100%')
400  }
401}
402```
403