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 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