1# TaskPool简介 2 3任务池(TaskPool)作用是为应用程序提供一个多线程的运行环境,降低整体资源的消耗、提高系统的整体性能,且您无需关心线程实例的生命周期。具体接口信息及使用方法详情请见[TaskPool](../reference/apis-arkts/js-apis-taskpool.md)。 4 5## TaskPool运作机制 6 7TaskPool运作机制示意图 8 9 10 11TaskPool支持开发者在宿主线程封装任务抛给任务队列,系统选择合适的工作线程,进行任务的分发及执行,再将结果返回给宿主线程。接口直观易用,支持任务的执行、取消,以及指定优先级的能力,同时通过系统统一线程管理,结合动态调度及负载均衡算法,可以节约系统资源。系统默认会启动一个任务工作线程,当任务较多时会扩容,工作线程数量上限跟当前设备的物理核数相关,具体数量内部管理,保证最优的调度及执行效率,长时间没有任务分发时会缩容,减少工作线程数量。 12 13## TaskPool注意事项 14 15- 实现任务的函数需要使用[@Concurrent装饰器](#concurrent装饰器)标注,且仅支持在.ets文件中使用。 16 17- 从API version 11开始,跨并发实例传递带方法的实例对象时,该类必须使用装饰器[@Sendable装饰器](arkts-sendable.md#sendable装饰器)标注,且仅支持在.ets文件中使用。 18 19- 任务函数在TaskPool工作线程的执行耗时不能超过3分钟(不包含Promise和async/await异步调用的耗时,例如网络下载、文件读写等I/O任务的耗时),否则会被强制退出。 20 21- 实现任务的函数入参需满足序列化支持的类型,详情请参见[线程间通信对象](interthread-communication-overview.md)。 22 23- ArrayBuffer参数在TaskPool中默认转移,需要设置转移列表的话可通过接口[setTransferList()](../reference/apis-arkts/js-apis-taskpool.md#settransferlist10)设置。 24 25- 由于不同线程中上下文对象是不同的,因此TaskPool工作线程只能使用线程安全的库,例如UI相关的非线程安全库不能使用。 26 27- 序列化传输的数据量大小限制为16MB。 28 29- [Priority](../reference/apis-arkts/js-apis-taskpool.md#priority)的IDLE优先级是用来标记需要在后台运行的耗时任务(例如数据同步、备份),它的优先级别是最低的。这种优先级标记的任务只会在所有线程都空闲的情况下触发执行,并且只会占用一个线程来执行。 30 31- Promise不支持跨线程传递,如果TaskPool返回pending或rejected状态的Promise,会返回失败;对于fulfilled状态的Promise,TaskPool会解析返回的结果,如果结果可以跨线程传递,则返回成功。 32 33- 不支持在TaskPool工作线程中使用[AppStorage](../quick-start/arkts-appstorage.md)。 34 35- TaskPool支持开发者在宿主线程封装任务抛给任务队列,理论上可以支持任意多的任务,但任务的执行受限于任务的优先级以及系统资源的影响,在工作线程扩容到最大后,可能会导致任务的执行效率下降。 36 37- TaskPool不支持指定任务所运行的线程,任务会被分配到空闲的线程中执行。如果需要指定任务运行的线程,建议使用[Worker](./worker-introduction.md)来实现。 38 39## \@Concurrent装饰器 40 41在使用[TaskPool](../reference/apis-arkts/js-apis-taskpool.md)时,执行的并发函数需要使用该装饰器修饰,否则无法通过相关校验。 42 43> **说明:** 44> 45> 从API version 9开始,支持使用\@Concurrent装饰器声明并校验并发函数。 46 47### 装饰器说明 48 49| \@Concurrent并发装饰器 | 说明 | 50| -------- | -------- | 51| 装饰器参数 | 无。 | 52| 使用场景 | 仅支持在Stage模型的工程中使用。仅支持在.ets文件中使用。 | 53| 装饰的函数类型 | 允许标注async函数或普通函数。禁止标注generator、箭头函数、类方法。不支持类成员函数或者匿名函数。 | 54| 装饰的函数内的变量类型 | 允许使用local变量、入参和通过import引入的变量。禁止使用闭包变量。 | 55| 装饰的函数内的返回值类型 | 支持的类型请查[线程间通信对象](interthread-communication-overview.md)。 | 56 57> **说明:** 58> 59> 由于\@Concurrent标记的函数不能访问闭包,因此\@Concurrent标记的函数内部不能调用当前文件的其他函数,例如: 60> 61> ```ts 62> function bar() { 63> } 64> 65> @Concurrent 66> function foo() { 67> bar(); // 违反闭包原则,报错 68> } 69> ``` 70 71### 装饰器使用示例 72 73#### 并发函数一般使用 74 75并发函数为一个计算两数之和的普通函数,taskpool执行该函数并返回结果。 76 77示例: 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#### 并发函数返回Promise 119 120并发函数中返回Promise的表现需关注,如下例所示,其中testPromise、testPromise1等并发同步函数会处理该Promise并返回结果。 121 122示例: 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#### 并发函数中使用自定义类或函数 230 231并发函数中使用自定义类或函数时需定义在不同文件,否则会被认为是闭包,如下例所示。 232 233示例: 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 // case1:在并发函数中直接调用同文件内定义的类或函数 259 260 // 直接调用同文件定义的函数add(),add飘红报错:Only imported variables and local variables can be used in @Concurrent decorated functions. <ArkTSCheck> 261 // add(1); 262 // 直接使用同文件定义的TestA构造,TestA飘红报错:Only imported variables and local variables can be used in @Concurrent decorated functions. <ArkTSCheck> 263 // let a = new TestA("aaa"); 264 // 直接访问同文件定义的TestB的成员nameStr,TestB飘红报错:Only imported variables and local variables can be used in @Concurrent decorated functions. <ArkTSCheck> 265 // console.info("TestB name is: " + TestB.nameStr); 266 267 // case2:在并发函数中调用定义在Test.ets文件并导入当前文件的类或函数 268 269 // 输出结果:res1 is: 2 270 console.info("res1 is: " + testAdd(1)); 271 let tmpStr = new MyTestA("TEST A"); 272 // 输出结果:res2 is: TEST A 273 console.info("res2 is: " + tmpStr.name); 274 // 输出结果: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#### 并发异步函数中使用Promise 329 330并发异步函数中如果使用Promise,建议搭配await使用。这样TaskPool会捕获Promise中可能发生的异常。推荐使用示例如下。 331 332示例: 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