1# TaskPool Introduction 2 3TaskPool provides a multithreaded environment for applications. It helps reduce 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 7TaskPool operating mechanism 8 9 10 11With TaskPool, you can encapsulate tasks in the main thread and throw the tasks to the task queue. The system selects proper worker threads to distribute and execute the tasks, and then returns the result to the main 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 a long period of time, the system reduces the number of worker threads. 12 13## Precautions for TaskPool 14 15- A task function must be decorated with [\@Concurrent](#concurrent-decorator) and can be used only in .ets files. 16 17- Since API version 11, when an instance object with a method is passed across concurrent instances, the class must be annotated with the decorator [@Sendable](arkts-sendable.md#sendable) and can be used only in .ets files. 18 19- A task function in the TaskPool worker thread must finish the execution within 3 minutes (excluding the time used for Promise or async/await asynchronous call, for example, the duration of I/O tasks such as network download and file read/write). Otherwise, it forcibly exits. 20 21- The input parameters of the function for implementing the task must meet the types supported by serialization. For details, see [Inter-Thread Communication Object](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, rather than UI-related non-thread-safe libraries. 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 (such as data synchronization and backup) that need to run in the background Tasks marked with **IDLE** are executed only when all threads are idle and occupy only one thread for execution. 30 31- Promises cannot be transferred across threads. If the TaskPool returns a promise in the pending or rejected state, a failure message is returned. For a promise in the fulfilled state, the 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 the TaskPool worker thread. 34 35## \@Concurrent Decorator 36 37To pass function verification, the 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| The decorator can be used only in projects of the stage model. It can be used only in .ets files.| 49| Decorated function types| This decorator can be used for asynchronous functions and common functions. It cannot be used for generators, arrow functions, or methods. It does not support class member functions or anonymous functions.| 50| Variable types in decorated functions| Local variables, input parameters, and variables imported through **import** are supported. Closure variables are not allowed.| 51| Return value types in decorated functions| For details about the supported types, see [Inter-Thread Communication Object](interthread-communication-overview.md).| 52 53> **Note** 54> 55> Functions marked by \@Concurrent cannot access closures. Therefore, functions marked by \@Concurrent cannot call other functions of the current file. For example: 56> 57> ```ts 58> function bar() { 59> } 60> 61> @Concurrent 62> function foo() { 63> bar (); // The closure principle is violated. An error is reported. 64> } 65> ``` 66 67### Decorator Example 68 69#### Commonly used by concurrent functions 70 71A concurrent function is a common function that calculates the sum of two numbers. The taskpool executes the function 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#### Promise returned by concurrent functions 115 116Pay attention to the performance of returning Promise in the concurrent function. The concurrent synchronous function processes and returns Promise with the result. 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 a User-Defined Class or Function in a Concurrent Function 226 227User-defined classes or functions used in concurrent functions must be defined in different 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 in a concurrent function. 255 256 // Directly call the add () function defined in the same file. The error message "Only imported variables and local variables can be used in @Concurrent decorated functions. <ArkTSCheck>" is displayed in red. 257 // add(1); 258 // Use the TestA defined in the same file. The error message "Only imported variables and local variables can be used in @Concurrent decorated functions. <ArkTSCheck>" is displayed. 259 // let a = new TestA("aaa"); 260 // Directly access the nameStr member of TestB defined in the same file. The error message "Only imported variables and local variables can be used in @Concurrent decorated functions. <ArkTSCheck>" is displayed in red. 261 // console.info("TestB name is: " + TestB.nameStr); 262 263 // Case 2: In the concurrent function, call the class or function defined in the Test.ets file and import the current file. 264 265 // Output result: res1 is: 2 266 console.info("res1 is: " + testAdd(1)); 267 let tmpStr = new MyTestA("TEST A"); 268 // Output result: 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 Promise in a Concurrent Asynchronous Function 325 326If Promise is used in the concurrent asynchronous function, you are advised to use await to capture exceptions that may occur in Promise. A recommended example is provided below. 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