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 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## \@Concurrent Decorator 36 37To pass function verification, concurrent functions executed in a [TaskPool](../reference/apis-arkts/js-apis-taskpool.md) must be decorated using \@Concurrent. 38 39> **NOTE** 40> 41> Since API version 9, the @Concurrent decorator can be used to declare and verify concurrent functions. 42 43### Decorator Description 44 45| \@Concurrent Decorator| Description| 46| -------- | -------- | 47| Decorator parameters| None.| 48| Use scenario| Used only in projects of the stage model and only in .ets files.| 49| 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.| 50| Variable types in decorated functions| Local variables, parameters, and variables imported via **import** are allowed. Closure variables are prohibited.| 51| Return value types in decorated functions| Supported types are listed in [Inter-Thread Communication](interthread-communication-overview.md). | 52 53> **NOTE** 54> 55> Functions decorated with \@Concurrent cannot access closures. Therefore, they cannot call other functions within the same file. The following provides an example. 56> 57> ```ts 58> function bar() { 59> } 60> 61> @Concurrent 62> function foo() { 63> bar(); // This violates the closure principle. An error is reported. 64> } 65> ``` 66 67### Decorator Usage Examples 68 69#### General Use of Concurrent Functions 70 71A concurrent function that calculates the sum of two numbers is executed by TaskPool and returns the result. 72 73Example: 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#### Concurrent Functions Returning Promises 115 116Pay 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. 117 118Example: 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#### Using Custom Classes or Functions in Concurrent Functions 226 227Custom 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. 228 229Example: 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 // Case 1: Directly call a class or function defined in the same file within a concurrent function. 255 256 // 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>" 257 // add(1); 258 // 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>" 259 // let a = new TestA("aaa"); 260 // 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>" 261 // console.info("TestB name is: " + TestB.nameStr); 262 263 // Case 2: In the concurrent function, call classes or functions defined in the Test.ets file and imported into the current file. 264 265 // Output: res1 is: 2 266 console.info("res1 is: " + testAdd(1)); 267 let tmpStr = new MyTestA("TEST A"); 268 // Output: res2 is: TEST A 269 console.info("res2 is: " + tmpStr.name); 270 // Output: 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#### Using Promises in Concurrent Asynchronous Functions 325 326If 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. 327 328Example: 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