• 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 encapsulate tasks in the host thread and submit the tasks to the task queue. The system selects appropriate worker threads to distribute and 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.
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) and are supported only in .ets files.
18
19- A task function must finish the execution in a TaskPool 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).
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).
24
25- The context objects in different threads are different. Therefore, TaskPool worker threads can use only thread-safe libraries. For example, non-thread-safe UI-related libraries cannot be used.
26
27- A maximum of 16 MB data can be serialized.
28
29- 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 and occupy only one thread for execution.
30
31- 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.
32
33- [AppStorage](../quick-start/arkts-appstorage.md) cannot be used in TaskPool worker threads.
34
35- 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 is influenced by the task priority and the availability of system resources. Once the Worker threads reach their maximum capacity, the efficiency of task execution might be compromised.
36
37- 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.
38
39## \@Concurrent Decorator
40
41To pass function verification, concurrent functions executed in a [TaskPool](../reference/apis-arkts/js-apis-taskpool.md) must be decorated using \@Concurrent.
42
43> **NOTE**
44>
45> Since API version 9, the @Concurrent decorator can be used to declare and verify concurrent functions.
46
47### Decorator Description
48
49| \@Concurrent Decorator| Description|
50| -------- | -------- |
51| Decorator parameters| None.|
52| Use scenario| Used only in projects of the stage model and only in .ets files.|
53| 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.|
54| Variable types in decorated functions| Local variables, parameters, and variables imported via **import** are allowed. Closure variables are prohibited.|
55| Return value types in decorated functions| Supported types are listed in [Inter-Thread Communication](interthread-communication-overview.md).|
56
57> **NOTE**
58>
59> Functions decorated with \@Concurrent cannot access closures. Therefore, they cannot call other functions within the same file. The following provides an example.
60>
61> ```ts
62> function bar() {
63> }
64>
65> @Concurrent
66> function foo() {
67> bar(); // This violates the closure principle. An error is reported.
68> }
69> ```
70
71### Decorator Usage Examples
72
73#### General Use of Concurrent Functions
74
75A concurrent function that calculates the sum of two numbers is executed by TaskPool and returns the result.
76
77Example:
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#### Concurrent Functions Returning Promises
119
120Pay attention to the behavior of returning Promises in concurrent functions. In the following example, concurrent functions like **testPromise** and **testPromise1** handle these Promises and return results.
121
122Example:
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#### Using Custom Classes or Functions in Concurrent Functions
230
231Custom 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.
232
233Example:
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  // Case 1: Directly call a class or function defined in the same file within a concurrent function.
259
260  // 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>"
261  // add(1);
262  // 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>"
263  // let a = new TestA("aaa");
264  // 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>"
265  // console.info("TestB name is: " + TestB.nameStr);
266
267  // Case 2: In the concurrent function, call classes or functions defined in the Test.ets file and imported into the current file.
268
269  // Output: res1 is: 2
270  console.info("res1 is: " + testAdd(1));
271  let tmpStr = new MyTestA("TEST A");
272  // Output: res2 is: TEST A
273  console.info("res2 is: " + tmpStr.name);
274  // Output: 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#### Using Promises in Concurrent Asynchronous Functions
329
330If 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.
331
332Example:
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