• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# TaskPool
2
3TaskPool is designed to provide a multithreaded runtime environment for applications. It helps reduce overall resource consumption and improve system performance. It also frees you from caring about the lifecycle of thread instances. For details about the APIs and their usage, see [TaskPool](../reference/apis-arkts/js-apis-taskpool.md).
4
5## TaskPool Operating Mechanism
6
7The figure below illustrates the operating mechanism of TaskPool.
8
9![image_0000001964858368](figures/image_0000001964858368.png)
10
11With TaskPool, you can submit tasks in the host thread to the task queue. The system selects appropriate worker threads to execute the tasks, and then returns the result to the host thread. TaskPool provides APIs to execute and cancel tasks, and set the task priority. It minimizes system resource usage through unified thread management, dynamic scheduling, and load balancing. By default, the system starts a worker thread and increases the thread quantity as the number of tasks increases. The maximum number of worker threads that can be created depends on the number of physical cores of the device. The actual number is managed internally to ensure optimal scheduling and execution efficiency. If no task is distributed for an extended period, the system reduces the number of worker threads. For details, see [TaskPool Scaling Mechanism](taskpool-introduction.md#taskpool-scaling-mechanism).
12
13## Precautions for TaskPool
14
15- Functions implementing tasks must be decorated with [\@Concurrent](#concurrent-decorator) and are supported only in .ets files.
16
17- Starting from API version 11, when passing instances with methods across concurrent instances, the class must be decorated with [@Sendable](arkts-sendable.md#sendable-decorator) and are supported only in .ets files.
18
19- A task function (except [LongTask](../reference/apis-arkts/js-apis-taskpool.md#longtask12)) must finish the execution in a TaskPool's worker thread within 3 minutes (excluding the time used for Promise or async/await asynchronous call, such as the duration of I/O tasks like network download and file read/write operation). Otherwise, it is forcibly terminated.
20
21- Parameters of functions implementing tasks must be of types supported by serialization. For details, see [Inter-Thread Communication](interthread-communication-overview.md). Currently, complex types decorated with [@State](../ui/state-management/arkts-state.md), [@Prop](../ui/state-management/arkts-prop.md), and [@Link](../ui/state-management/arkts-link.md) are not supported.
22
23- Parameters of the ArrayBuffer type are transferred in TaskPool by default. You can set the transfer list by calling [setTransferList()](../reference/apis-arkts/js-apis-taskpool.md#settransferlist10). If you need to call a task that uses ArrayBuffer as a parameter multiple times, call [setCloneList()](../reference/apis-arkts/js-apis-taskpool.md#setclonelist11) to change the transfer behavior of ArrayBuffer in the thread to pass-by-copy, avoiding affecting the original object.
24
25  ```ts
26  import { taskpool } from '@kit.ArkTS';
27  import { BusinessError } from '@kit.BasicServicesKit';
28
29  @Concurrent
30  function printArrayBuffer(buffer: ArrayBuffer) {
31    return buffer;
32  }
33
34  function testArrayBuffer() {
35    const buffer = new ArrayBuffer(1);
36    const group = new taskpool.TaskGroup();
37    const task = new taskpool.Task(printArrayBuffer, buffer);
38    group.addTask(task);
39    task.setCloneList([buffer]);
40    for (let i = 0; i < 5; i++) {
41      taskpool.execute(group).then(() => {
42        console.info('execute group success');
43      }).catch((e: BusinessError) => {
44        console.error(`execute group error: ${e.message}`);
45      })
46    }
47  }
48  ```
49
50- The context objects in different threads are different. Therefore, a TaskPool's worker threads can use only thread-safe libraries. For example, non-thread-safe UI-related libraries cannot be used.
51
52- A maximum of 16 MB data can be serialized.
53
54- Among all the values of [Priority](../reference/apis-arkts/js-apis-taskpool.md#priority), **IDLE** is used to mark time-consuming tasks that need to run in the background (such as data synchronization and backup), and it has the lowest priority. Tasks marked with this priority are executed only when all threads are idle. Only one task with this priority can be executed at a time.
55
56- Promises cannot be transferred across threads. If TaskPool returns a Promise in the pending or rejected state, a failure message is returned. For a Promise in the fulfilled state, TaskPool parses the returned result. If the result can be transferred across threads, a success message is returned.
57
58- [AppStorage](../ui/state-management/arkts-appstorage.md) cannot be used in the TaskPool's worker threads.
59
60- TaskPool allows you to package tasks in the host thread and submit them to the task queue. While it can theoretically handle an unlimited number of tasks, the actual task execution efficiency is influenced by the task priority and system resources. Once the worker threads reach their maximum capacity, the efficiency of task execution might be compromised.
61
62- TaskPool does not allow you to specify the thread where a task runs. Instead, tasks are assigned to run in available threads. If you want to specify the thread for running a task, using [Worker](worker-introduction.md) is a better approach.
63
64## \@Concurrent Decorator
65
66To pass function verification, concurrent functions executed in a [TaskPool](../reference/apis-arkts/js-apis-taskpool.md) must be decorated using \@Concurrent.
67
68> **NOTE**
69>
70> Since API version 9, the @Concurrent decorator can be used to declare and verify concurrent functions.
71
72### Decorator Description
73
74| \@Concurrent Decorator| Description|
75| -------- | -------- |
76| Decorator parameters| None.|
77| Use scenario| Used only in projects of the stage model and only in .ets files.|
78| Decorated function types| Used for async functions or regular functions. It cannot be used for generators, arrow functions, or class methods. It does not support class member functions or anonymous functions.|
79| Variable types in decorated functions| Local variables, parameters, and variables imported via **import** are allowed. Closure variables are prohibited.|
80| Return value types in decorated functions| Supported types are listed in [Inter-Thread Communication](interthread-communication-overview.md).|
81
82> **NOTE**
83>
84> Functions decorated with \@Concurrent cannot access closures. Therefore, they cannot call other functions within the same file. The following provides an example.
85>
86> ```ts
87> function bar() {
88> }
89>
90> @Concurrent
91> function foo() {
92>   bar(); // This violates the closure principle. An error is reported.
93> }
94> ```
95
96### Decorator Usage Examples
97
98#### General Use of Concurrent Functions
99
100A concurrent function that calculates the sum of two numbers is executed by TaskPool and returns the result.
101
102Example:
103
104```ts
105import { taskpool } from '@kit.ArkTS';
106
107@Concurrent
108function add(num1: number, num2: number): number {
109  return num1 + num2;
110}
111
112async function concurrentFunc(): Promise<void> {
113  try {
114    const task: taskpool.Task = new taskpool.Task(add, 1, 2);
115    console.info(`taskpool res is: ${await taskpool.execute(task)}`); // Output: taskpool res is: 3
116  } catch (e) {
117    console.error(`taskpool execute error is: ${e}}`);
118  }
119}
120
121@Entry
122@Component
123struct Index {
124  @State message: string = 'Hello World';
125
126  build() {
127    Row() {
128      Column() {
129        Text(this.message)
130          .fontSize(50)
131          .fontWeight(FontWeight.Bold)
132          .onClick(() => {
133            concurrentFunc();
134          })
135      }
136      .width('100%')
137    }
138    .height('100%')
139  }
140}
141```
142<!-- @[concurrent_taskpool_common_usage](https://gitee.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/ArkTsConcurrent/MultithreadedConcurrency/TaskPoolIntroduction/entry/src/main/ets/managers/generaluse.ets) -->
143
144#### Concurrent Functions Returning Promises
145
146Pay attention to the Promises returned by concurrent functions. In the following example, **testPromise** and **testPromise1** handle these Promises and return results.
147
148Example:
149
150```ts
151import { taskpool } from '@kit.ArkTS';
152
153@Concurrent
154function testPromise(args1: number, args2: number): Promise<number> {
155  return new Promise<number>((resolve, reject) => {
156    resolve(args1 + args2);
157  });
158}
159
160@Concurrent
161async function testPromise1(args1: number, args2: number): Promise<number> {
162  return new Promise<number>((resolve, reject) => {
163    resolve(args1 + args2);
164  });
165}
166
167@Concurrent
168async function testPromise2(args1: number, args2: number): Promise<number> {
169  return await new Promise<number>((resolve, reject) => {
170    resolve(args1 + args2);
171  });
172}
173
174@Concurrent
175function testPromise3() {
176  return Promise.resolve(1);
177}
178
179@Concurrent
180async function testPromise4(): Promise<number> {
181  return 1;
182}
183
184@Concurrent
185async function testPromise5(): Promise<string> {
186  return await new Promise((resolve) => {
187    setTimeout(() => {
188      resolve('Promise setTimeout after resolve');
189    }, 1000)
190  });
191}
192
193async function testConcurrentFunc() {
194  const task1: taskpool.Task = new taskpool.Task(testPromise, 1, 2);
195  const task2: taskpool.Task = new taskpool.Task(testPromise1, 1, 2);
196  const task3: taskpool.Task = new taskpool.Task(testPromise2, 1, 2);
197  const task4: taskpool.Task = new taskpool.Task(testPromise3);
198  const task5: taskpool.Task = new taskpool.Task(testPromise4);
199  const task6: taskpool.Task = new taskpool.Task(testPromise5);
200
201  taskpool.execute(task1).then((d: object) => {
202    console.info(`task1 res is: ${d}`); // Output: task1 res is: 3
203  }).catch((e: object) => {
204    console.error(`task1 catch e: ${e}`);
205  })
206  taskpool.execute(task2).then((d: object) => {
207    console.info(`task2 res is: ${d}`);
208  }).catch((e: object) => {
209    console.error(`task2 catch e: ${e}`); // Output: task2 catch e: Error: Can't return Promise in pending state
210  })
211  taskpool.execute(task3).then((d: object) => {
212    console.info(`task3 res is: ${d}`); // Output: task3 res is: 3
213  }).catch((e: object) => {
214    console.error(`task3 catch e: ${e}`);
215  })
216  taskpool.execute(task4).then((d: object) => {
217    console.info(`task4 res is: ${d}`); // Output: task4 res is: 1
218  }).catch((e: object) => {
219    console.error(`task4 catch e: ${e}`);
220  })
221  taskpool.execute(task5).then((d: object) => {
222    console.info(`task5 res is: ${d}`); // Output: task5 res is: 1
223  }).catch((e: object) => {
224    console.error(`task5 catch e: ${e}`);
225  })
226  taskpool.execute(task6).then((d: object) => {
227    console.info(`task6 res is: ${d}`); // Output: task6 res is: Promise setTimeout after resolve
228  }).catch((e: object) => {
229    console.error(`task6 catch e: ${e}`);
230  })
231}
232
233@Entry
234@Component
235struct Index {
236  @State message: string = 'Hello World';
237
238  build() {
239    Row() {
240      Column() {
241        Button(this.message)
242          .fontSize(50)
243          .fontWeight(FontWeight.Bold)
244          .onClick(() => {
245            testConcurrentFunc();
246          })
247      }
248      .width('100%')
249    }
250    .height('100%')
251  }
252}
253```
254<!-- @[concurrent_taskpool_promise_return](https://gitee.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/ArkTsConcurrent/MultithreadedConcurrency/TaskPoolIntroduction/entry/src/main/ets/managers/returnpromise.ets) -->
255
256#### Using Custom Classes or Functions in Concurrent Functions
257
258Custom classes or functions used in concurrent functions must be defined in separate files. Otherwise, they will be considered as closures, as shown in the following example.
259
260Example:
261
262```ts
263// Index.ets
264import { taskpool } from '@kit.ArkTS';
265import { BusinessError } from '@kit.BasicServicesKit';
266import { testAdd, MyTestA, MyTestB } from './Test';
267
268function add(arg: number) {
269  return ++arg;
270}
271
272class TestA {
273  constructor(name: string) {
274    this.name = name;
275  }
276
277  name: string = 'ClassA';
278}
279
280class TestB {
281  static nameStr: string = 'ClassB';
282}
283
284@Concurrent
285function TestFunc() {
286  // Case 1: Directly call a class or function defined in the same file within a concurrent function.
287
288  // Directly call the add() function defined in the same file. The following error message is displayed: "Only imported variables and local variables can be used in @Concurrent decorated functions. <ArkTSCheck>"
289  // add(1);
290  // Directly use the TestA constructor defined in the same file. The following error message is displayed: "Only imported variables and local variables can be used in @Concurrent decorated functions. <ArkTSCheck>"
291  // const a = new TestA('aaa');
292  // Directly access the nameStr member of TestB defined in the same file. The following error message is displayed: "Only imported variables and local variables can be used in @Concurrent decorated functions. <ArkTSCheck>"
293  // console.info(`TestB name is: ${TestB.nameStr}`);
294
295  // Case 2: In the concurrent function, call classes or functions defined in the Test.ets file and imported into the current file.
296
297  // Output: res1 is: 2
298  console.info(`res1 is: ${testAdd(1)}`);
299  const tmpStr = new MyTestA('TEST A');
300  // Output: res2 is: TEST A
301  console.info(`res2 is: ${tmpStr.name}`);
302  // Output: res3 is: MyTestB
303  console.info(`res3 is: ${MyTestB.nameStr}`);
304}
305
306
307@Entry
308@Component
309struct Index {
310  @State message: string = 'Hello World';
311
312  build() {
313    RelativeContainer() {
314      Text(this.message)
315        .id('HelloWorld')
316        .fontSize(50)
317        .fontWeight(FontWeight.Bold)
318        .alignRules({
319          center: { anchor: '__container__', align: VerticalAlign.Center },
320          middle: { anchor: '__container__', align: HorizontalAlign.Center }
321        })
322        .onClick(() => {
323          const task = new taskpool.Task(TestFunc);
324          taskpool.execute(task).then(() => {
325            console.info('taskpool: execute task success!');
326          }).catch((e: BusinessError) => {
327            console.error(`taskpool: execute: Code: ${e.code}, message: ${e.message}`);
328          })
329        })
330    }
331    .height('100%')
332    .width('100%')
333  }
334}
335```
336<!-- @[concurrent_taskpool_custom_class_function](https://gitee.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/ArkTsConcurrent/MultithreadedConcurrency/TaskPoolIntroduction/entry/src/main/ets/managers/customclasses.ets) -->
337
338```ts
339// Test.ets
340export function testAdd(arg: number) {
341  return ++arg;
342}
343
344@Sendable
345export class MyTestA {
346  constructor(name: string) {
347    this.name = name;
348  }
349  name: string = 'MyTestA';
350}
351
352export class MyTestB {
353  static nameStr:string = 'MyTestB';
354}
355```
356<!-- @[concurrent_taskpool_test_resources](https://gitee.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/ArkTsConcurrent/MultithreadedConcurrency/TaskPoolIntroduction/entry/src/main/ets/managers/Test.ets) -->
357
358#### Using Promises in Concurrent Asynchronous Functions
359
360If Promise is used in concurrent asynchronous functions, you are advised to use await to enable TaskPool to capture any exceptions that may occur within the Promise. The recommended usage is shown in the following example.
361
362Example:
363
364```ts
365import { taskpool } from '@kit.ArkTS';
366
367@Concurrent
368async function testPromiseError() {
369  await new Promise<number>((resolve, reject) => {
370    resolve(1);
371  }).then(() => {
372    throw new Error('testPromise Error');
373  })
374}
375
376@Concurrent
377async function testPromiseError1() {
378  await new Promise<string>((resolve, reject) => {
379    reject('testPromiseError1 Error msg');
380  })
381}
382
383@Concurrent
384function testPromiseError2() {
385  return new Promise<string>((resolve, reject) => {
386    reject('testPromiseError2 Error msg');
387  })
388}
389
390async function testConcurrentFunc() {
391  const task1: taskpool.Task = new taskpool.Task(testPromiseError);
392  const task2: taskpool.Task = new taskpool.Task(testPromiseError1);
393  const task3: taskpool.Task = new taskpool.Task(testPromiseError2);
394
395  taskpool.execute(task1).then((d: object) => {
396    console.info(`task1 res is: ${d}`);
397  }).catch((e: object) => {
398    console.error(`task1 catch e: ${e}`); // task1 catch e: Error: testPromise Error
399  })
400  taskpool.execute(task2).then((d: object) => {
401    console.info(`task2 res is: ${d}`);
402  }).catch((e: object) => {
403    console.error(`task2 catch e: ${e}`); // task2 catch e: testPromiseError1 Error msg
404  })
405  taskpool.execute(task3).then((d: object) => {
406    console.info(`task3 res is: ${d}`);
407  }).catch((e: object) => {
408    console.error(`task3 catch e: ${e}`); // task3 catch e: testPromiseError2 Error msg
409  })
410}
411
412@Entry
413@Component
414struct Index {
415  @State message: string = 'Hello World';
416
417  build() {
418    Row() {
419      Column() {
420        Button(this.message)
421          .fontSize(50)
422          .fontWeight(FontWeight.Bold)
423          .onClick(() => {
424            testConcurrentFunc();
425          })
426      }
427      .width('100%')
428    }
429    .height('100%')
430  }
431}
432```
433<!-- @[concurrent_taskpool_async_promise_usage](https://gitee.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/ArkTsConcurrent/MultithreadedConcurrency/TaskPoolIntroduction/entry/src/main/ets/managers/asynchronousfunctions.ets) -->
434
435## TaskPool Scaling Mechanism
436
437### Expansion Mechanism
438
439Generally, when you submit tasks to the task queue, an expansion check is triggered. The expansion check first determines whether the number of idle worker threads is greater than the number of tasks. If it is, there are idle worker threads in the thread pool, and no expansion is needed. Otherwise, the required number of worker threads is calculated based on the load, and new threads are created accordingly.
440
441### Contraction Mechanism
442
443After expansion, TaskPool creates multiple worker threads. However, when the number of tasks decreases, these threads become idle, leading to resource wastage. Therefore, TaskPool provides a contraction mechanism. TaskPool employs a timer to periodically check the current load. The timer is triggered every 30 seconds, and each time it attempts to release idle worker threads. A thread can be released if it meets the following conditions:
444
445- The thread has been idle for at least 30 seconds.
446
447- The thread has not executed any [long tasks](../reference/apis-arkts/js-apis-taskpool.md#longtask12).
448
449- There are no service requests or unreleased handles on the thread, such as [Timers](../reference/common/js-apis-timer.md).
450
451- The thread is not in a debugging or optimization phase.
452
453- There are no created but not yet destroyed child Worker thread in the thread.
454