• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 并发常见问题
2<!--Kit: ArkTS-->
3<!--Subsystem: CommonLibrary-->
4<!--Owner: @lijiamin2025-->
5<!--Designer: @weng-changcheng-->
6<!--Tester: @kirl75; @zsw_zhushiwei-->
7<!--Adviser: @ge-yafang-->
8
9## TaskPool任务不执行快速定位指导
10
11开发者发现TaskPool任务不执行时,可按照以下步骤快速定位。
12
131. **taskpool.execute接口是否调用**。
14
15   taskpool.execute被调用时,Hilog会打印TaskPool调用态日志(Task Allocation: taskId:)。
16   如果发现没有该维测日志表明taskpool.execute实际未调用,应用需排查taskpool.execute之前的其他业务逻辑是否执行完成。
17
18   ```ts
19   import { taskpool } from '@kit.ArkTS';
20
21   @Concurrent
22   function createTask(a: number, b:number): number {
23     let sum = a + b;
24     return sum;
25   }
26
27   @Entry
28   @Component
29   struct Index {
30     @State message: string = 'Hello World';
31
32     build() {
33       Row() {
34         Column() {
35           Text(this.message)
36             .fontSize(50)
37             .fontWeight(FontWeight.Bold)
38             .onClick(() => {
39               console.info("test start");
40               // 其他业务逻辑
41               // ...
42               let task: taskpool.Task = new taskpool.Task(createTask, 1, 2);
43               taskpool.execute(task);
44               // ...
45             })
46         }
47         .width('100%')
48       }
49       .height('100%')
50     }
51   }
52
53   // 如果test start在控制台打印,但是并未出现Task Allocation: taskId:的日志,则taskpool.execute没有执行,应用需要排查其他业务逻辑。
54   ```
55
562. **TaskPool任务是否被执行**。
57
58   调用taskpool.execute接口会打印TaskPool**调用态日志**(Task Allocation: taskId:)。
59   定位到目标任务对应的Task Allocation: taskId:日志后,在日志中搜索taskId后跟随的Id号,正常情况会打印**执行态日志**(Task Perform: name:)和**结束态日志**(Task PerformTask End: taskId:)。
60
61   1.  如果只有调用态日志,没有执行态日志。可能是由于先执行的TaskPool任务阻塞了TaskPool工作线程,导致TaskPool工作线程不可用,后执行的TaskPool任务无法执行。应用可以排查自身业务逻辑,或者通过trace进一步定位。
62
63   2. 如果只有调用态日志和执行态日志,没有结束态日志。应用优先分析自定义的TaskPool任务内的业务逻辑是否存在阻塞操作。
64
65   3. 如果调用态日志和执行态日志时间间隔较久,且应用关注任务的执行时机,可以按照以下步骤继续分析。
66
67      1. 查看是否发生大量TaskPool任务堆积未执行的情况。如果在较短时间内执行大量任务(出现大量调用态日志),后执行的任务需要等待前置任务执行完。此时可以检查TaskPool的扩容情况,如果在调用态日志打印之前,TaskPool工作线程数量已扩容到接近上限(上限数量为日志片段log2中的maxThreads字段),则可能是短时间内任务数量太多导致,应用可以通过合理设置优先级将重要任务和有时效要求的任务优先执行。
68
69      2. 查看前置执行的TaskPool任务是否本身耗时较长或者发生阻塞。如果前置任务本身耗时较长,应用可以通过合理设置优先级解决。如果前置任务发生了意料之外的阻塞(一段时间后阻塞解除),应用需要排查自身业务逻辑。
70
71   ```ts
72   // hilog 日志片段(模拟),格式如下,具体数值由应用运行时决定
73   // log1: 大量任务提交
74   taskpool:: Task Allocation: taskId: , priority: , executeState:
75   taskpool:: Task Allocation: taskId: , priority: , executeState:
76   taskpool:: Task Allocation: taskId: , priority: , executeState:
77   taskpool:: Task Allocation: taskId: , priority: , executeState:
78   taskpool:: Task Allocation: taskId: , priority: , executeState:
79   ...
80   // log2: 扩容日志
81   taskpool:: maxThreads: , created num: , total num:
82   // log3: 执行态日志
83   taskpool:: Task Perform: name: , taskId: , priority:
84   ```
85
863. **TaskPool任务执行时是否发生异常**。
87
88   1. 如果在执行TaskPool任务过程中发生JS异常,TaskPool会捕获该JS异常并通过taskpool.execute().catch((e:Error)=>{})将异常信息返回,应用需要查看异常信息并修复。
89
90      ```ts
91      import { taskpool } from '@kit.ArkTS';
92
93      @Concurrent
94      function createTask(a: number, b:number) {
95        let sum = a + b;
96        return sum;
97      }
98
99      @Entry
100      @Component
101      struct Index {
102        @State message: string = 'Hello World';
103
104        build() {
105          Row() {
106            Column() {
107              Text(this.message)
108                .fontSize(50)
109                .fontWeight(FontWeight.Bold)
110                .onClick(() => {
111                  console.info("test start");
112                  // 其他业务逻辑
113                  // ...
114                  let task: taskpool.Task = new taskpool.Task(createTask, 1, 2);
115                  taskpool.execute(task).then((res: object)=>{
116                    // 任务执行完处理结果
117                    // ...
118                  }).catch((e: Error)=>{
119                    // 任务发生异常后处理异常
120                    // ...
121                  })
122                  // ...
123                })
124            }
125            .width('100%')
126          }
127          .height('100%')
128        }
129      }
130      ```
131
132   2. 如果.catch分支无异常信息返回,但是应用通过TaskPool任务实现的功能发生问题,应用需要查看TaskPool任务逻辑是否发生阻塞,导致功能异常。
133
134## TaskPool任务执行慢排查思路
135
136开发者发现TaskPool任务的调用态日志(如taskpool::add taskId:或者taskpool::Task Allocation: taskId:)与执行态日志(taskpool::Task Perform: name:)间隔时间较长时,可参考该排查指导进行问题定位。
137
138### 排查方向:出问题的TaskPool任务优先级较低,应用后续新增较多优先级更高的TaskPool任务,导致原有低优先级的TaskPool任务执行延后
139
140**场景示例一**
141
142某应用创建了低优先级的TaskPool任务,且其他业务场景依赖这个任务执行。后续,应用又创建了很多中优先级任务,导致原有的低优先级任务执行时机延后,其他业务场景未按计划时间点完成任务。
143
144**解决方案**
145
146对完成执行时间点有要求的任务以低优先级执行不是好的选择,应用需要根据业务场景设置合理的任务优先级,且合理搭配任务优先级。
147
148**场景示例二**
149
150应用对某个TaskPool任务(简称taskA)执行时间有要求,执行超过5s时有检测机制。应用将taskA设置为MEDIUM优先级,在taskA前以MEDIUM优先级执行了较多其他任务,且这些任务耗时3s/5s不等,将已有线程和新扩容的线程均占满,导致taskA从taskpool.execute到执行结束超过5s。
151
152**解决方案**
153
1541.分析其他任务执行耗时3s/5s是否合理;2.调整taskA优先级。
155
156### 排查方向:晚执行的TaskPool任务是串行任务或者依赖其他任务
157
158**场景示例**
159
160如果问题场景对应的TaskPool任务是串行队列任务,查看该串行队列内前面任务的执行情况。如日志片段1所示该串行队列有四个任务,问题场景对应的是第四个任务,查看日志片段2发现第二个任务执行了2s,对于应用业务逻辑是不正常的。
161```ts
162// hilog 日志片段1(模拟)
163// seqRunner共有四个任务
164taskpool:: taskId 389508780288 in seqRunner 393913878464 immediately.
165taskpool:: add taskId: 394062838784 to seqRunner 393913878464
166taskpool:: add taskId: 393918679936 to seqRunner 393913878464
167taskpool:: add taskId: 393918673408 to seqRunner 393913878464
168
169// hilog 日志片段2(模拟)
170// 查看第二个任务, 发现任务执行到执行结束间隔2s
17118:28:28.223 taskpool:: taskId 394062838784 in seqRunner 393913878464 immediately.
17218:28:28.224 taskpool:: Task Perform: name : , taskId : 394062838784, priority : 0
17318:28:30.243 taskpool:: Task Perform End: taskId : 394062838784, performResult : Successful
174```
175
176**解决方案**
177
178应用继续分析第二个任务中的业务逻辑是否存在耗时操作。
179
180### 排查方向:@Concurrent标记的方法所在的ets文件里import过多模块
181
182**场景示例**
183
184TaskPool第一次执行任务慢,间隔几百毫秒,原因是子线程反序列化之前,会将@Concurrent标记的方法所在的ets文件import的所有模块都初始化,导致出现任务调度慢的情况。应用可通过trace进一步定位,如果反序列化成功前有许多init module的trace,应用自行排查ets文件是否import过多模块。
185
186**解决方案**
187
1881.可拆分@Concurrent方法到单独的ets文件,减少模块初始化时间;2.使用延迟加载([lazy import](arkts-lazy-import.md))。
189
190## TaskPool序列化失败问题定位指导
191
192**问题现象**
193
1941. JS异常
195
196   入参序列化失败
197
198   ```ts
199   // API version 20之前版本
200   Error message:An exception occurred during serialization, taskpool: failed to serialize arguments.
201
202   // API version 20及之后版本
203   Error message:An exception occurred during serialization, taskpool: failed to serialize arguments.
204   Serialize error: Serialize don't support object type:
205   ```
206
207   返回结果序列化失败
208
209   ```ts
210   // API version 20之前版本
211   Error message:An exception occurred during serialization, taskpool: failed to serialize result.
212
213   // API version 20及之后版本
214   Error message:An exception occurred during serialization, taskpool: failed to serialize result.
215   Serialize error: Serialize don't support object type:
216   ```
217
2182. Hilog错误日志
219
220   ```ts
221   // API version 20之前版本
222   [ecmascript] Unsupport serialize object type:
223   [ecmascript] ValueSerialize: serialize data is incomplete
224
225   // API version 20及之后版本
226   [ecmascript] Serialize don't support object type:
227   [ecmascript] ValueSerialize: serialize data is incomplete
228   ```
229
230**问题原因**
231
232TaskPool实现任务的函数(Concurrent函数)入参和返回结果需满足线程间通信支持的对象类型,详情请查看[线程间通信对象](../reference/apis-arkts/js-apis-taskpool.md#序列化支持类型)。当Concurrent函数的入参或返回结果是线程间通信不支持的对象类型时,会出现上述现象。应用可以结合Hilog日志中打印的对象类型进一步排查通信对象是否符合要求。
233
234**场景示例**
235
2361. 应用在启动TaskPool任务时,在Concurrent函数中传入线程间通信不支持的对象类型,导致抛出入参序列化失败异常。
237**解决方案**:应用需要查看[线程间通信对象](../reference/apis-arkts/js-apis-taskpool.md#序列化支持类型)排查Concurrent函数入参。
238
2392. 应用在启动TaskPool任务时,抛出入参序列化失败异常,同时Hilog打印错误日志Unsupport serialize object type: Proxy(API version 20及之后版本打印错误日志:Serialize error: Serialize don't support object type: Proxy)。基于错误日志可知应用在Concurrent函数中传入代理对象,排查代码发现入参使用了@State装饰器,导致原对象实际上变为Proxy代理对象,代理对象不属于线程间通信支持的对象类型。
240**解决方案**:TaskPool不支持@State、@Prop等装饰器修饰的复杂类型,具体内容可见[TaskPool注意事项](taskpool-introduction.md#taskpool注意事项)。应用需要去掉@State装饰器。
241
2423. 应用执行TaskPool任务时,抛出返回结果序列化失败异常,排查代码发现Concurrent函数返回结果是不支持的序列化类型。
243
244   ```ts
245   // utils.ets
246   @Concurrent
247   export function printArgs(args: number) {
248     return args;
249   }
250
251   // index.ets
252   import { taskpool } from '@kit.ArkTS'
253   import { BusinessError } from '@kit.BasicServicesKit'
254   import { printArgs} from './utils'
255   @Concurrent
256   function createTask(a: number, b:number) {
257     let sum = a + b;
258     // task1: 不支持的序列化类型
259     let task1: taskpool.Task = new taskpool.Task(printArgs, sum);
260     return task1;
261   }
262
263   function executeTask() {
264     // task
265     let task: taskpool.Task = new taskpool.Task(createTask, 1, 2);
266     taskpool.execute(task).then((res) => {
267     }).catch((e: BusinessError) => {
268       // 打印“返回结果序列化失败”异常信息
269       console.error("execute task failed " + e.message);
270     })
271   }
272   ```
273
274   **解决方案**:task1在.then中创建执行,Concurrent函数的返回结果设置为可序列化的类型。
275
276   ```ts
277   // utils.ets
278   @Concurrent
279   export function printArgs(args: number) {
280     return args;
281   }
282
283   // index.ets
284   import { taskpool } from '@kit.ArkTS'
285   import { BusinessError } from '@kit.BasicServicesKit'
286   import { printArgs} from './utils'
287   @Concurrent
288   function createTask(a: number, b:number) {
289     // 支持的序列化类型
290     let sum = a + b;
291     return sum;
292   }
293
294   function executeTask() {
295     // task
296     let task: taskpool.Task = new taskpool.Task(createTask, 1, 2);
297     taskpool.execute(task).then((res) => {
298       // task1
299       let task1: taskpool.Task = new taskpool.Task(printArgs, res);
300     }).catch((e: BusinessError) => {
301       console.error("execute task failed " + e.message);
302     })
303   }
304   ```
305
306## Sendable类A的实例对象a传递到子线程后,使用a instanceof A判断返回false
307
308应用在子线程使用instanceof接口时,需要在导出Sendable类A的ets文件使用"use shared"指令标记该模块为[共享模块](../arkts-utils/arkts-sendable-module.md)。
309
310**代码示例**
311
312```ts
313// pages/index.ets
314import { worker, ErrorEvent } from '@kit.ArkTS'
315import { A } from './sendable'
316const workerInstance = new worker.ThreadWorker('../workers/Worker.ets');
317function testInstanceof() {
318  let a = new A();
319  if (a instanceof A) {
320    // 打印test instanceof in main thread success
321    console.info("test instanceof in main thread success");
322  } else {
323    console.info("test instanceof in main thread failed");
324  }
325  workerInstance.postMessageWithSharedSendable(a);
326  workerInstance.onerror = (err: ErrorEvent) => {
327    console.error("worker err :" + err.message)
328  }
329}
330
331testInstanceof()
332```
333```ts
334// pages/sendable.ets
335"use shared"
336@Sendable
337export class A {
338    name: string = "name";
339    printName(): string {
340        return this.name;
341    }
342}
343```
344```ts
345// workers/Worker.ets
346import { A } from '../pages/sendable'
347import { worker, ThreadWorkerGlobalScope, MessageEvents } from '@kit.ArkTS'
348
349const workerPort: ThreadWorkerGlobalScope = worker.workerPort;
350workerPort.onmessage = (e: MessageEvents) => {
351    let a : A = e.data as A;
352    if (a instanceof A) {
353        // 打印test instanceof in worker thread success
354        console.info("test instanceof in worker thread success");
355    } else {
356        console.info("test instanceof in worker thread failed");
357    }
358}
359```
360
361## 使用Sendable特性抛JS异常排查指导
362
363由于Sendable特性存在固定布局、Sendable无法持有非Sendable等规格限制,开发者在进行Sendable改造时可能触发相关约束,导致抛出相应的JS异常。应用可参考以下内容进行代码排查。
364
365### 属性类型不一致异常
366
367**问题现象**
368
369```ts
370JS异常:TypeError: Cannot set sendable property with mismatched type
371```
372
373**问题原因与解决方案**
374
375由于ArkTS运行时在属性赋值时会严格进行类型一致性校验,如果定义的属性类型与传入的对象类型不一致,会抛出上述JS异常。应用需要基于JS异常栈信息,定位排查相应的业务逻辑。
376
377**场景示例**
378
3791. 应用在向子线程传递Sendable类A的实例对象时,抛出类型不一致异常。基于JS栈定位到问题发生在创建类A的实例对象时,排查后发现应用当前模块与其他模块联调时,其他模块未使用Sendable类B封装数据集。
380**解决方案** : 应用当前模块将其他模块传递的数据使用Sendable类重新封装。
381
382   ```ts
383   @Sendable
384   export class B {
385     constructor() {}
386   }
387
388   @Sendable
389   export class A {
390     constructor(b: B) {
391       this.b = b;
392     }
393     public b: B | undefined = undefined;
394   }
395   ```
396
3972. 应用查看JS异常栈发现运行this.g = g赋值语句时,抛出类型不一致异常。排查代码后发现属性g使用了@State装饰器,导致原对象变为Proxy代理对象,造成定义类型与传入类型不一致。
398**解决方案**:去掉@State装饰器
399
400### 新增属性异常
401
402**问题现象**
403
404```ts
405JS异常:TypeError: Cannot add property in prevent extensions
406```
407
408**问题原因与解决方案**
409
410由于Sendable类的布局固定,不允许增删属性,对Sendable对象新增属性时会抛出上述JS异常。应用需要基于JS异常栈定位到对应的ts文件代码行,排查相应的业务逻辑。
411
412**异常场景示例**
413
4141. 应用基于业务需要在同一个ets文件定义了同名的Sendable类和namespace,抛出新增属性异常。由于ts会合并同名的class和namespace,将namespace中的导出的内容附加到同名类上,实际上是对Sendable类新增属性,导致抛出上述异常。
415**解决方案**:规格限制,暂不支持合并同名Sendable class和namespace。
416
4172. 应用在HAR中使用Sendable特性时,抛出新增属性异常。查看JS异常栈,发现异常代码行定位在js文件,而Sendable特性不支持在js文件中使用,导致抛出非预期的异常。
418**解决方案**:在HAR中使用Sendable特性时,[配置tsHAR](../quick-start/har-package.md#编译生成ts文件)。
419
4203. 应用在Local Test单元测试或预览器中使用Sendable特性时,抛出新增属性异常。由于Sendable特性暂不支持在Local Test和预览器中使用,导致抛出非预期的异常。
421**解决方案**:规格限制,暂不支持。
422
423## ArkTS提供的Promise能力的原理是什么
424
425Promise是ArkTS提供的异步并发能力,是标准的JS语法。详情请查看[Promise](async-concurrency-overview.md#promise)概述。
426
427## TaskPool线程是否可以执行不需要@Concurrent和@Sendable修饰的JS闭包函数
428
429TaskPool执行的任务函数必须使用@Concurrent装饰器修饰,由于Concurrent函数不能访问闭包,因此函数内不可调用当前文件的其他普通函数,详情请参考[TaskPool注意事项](taskpool-introduction.md#taskpool注意事项)。但是,开发者可以通过给Concurrent函数传参的方式,传入@Sendable装饰器修饰的普通function和Async function,在Concurrent函数内调用Sendable function。
430
431因此,TaskPool线程目前不支持执行普通的JS闭包函数。如果有相关诉求,开发者可以根据业务需要使用[Worker](worker-introduction.md)并发能力进行业务改造。
432
433## TaskPool任务执行后的结果如何保存到自定义的数据结构
434
435**问题描述**
436
437TaskPool的任务执行函数Concurrent函数只能使用局部变量和函数入参,TaskPool任务执行后的结果应该如何保存到自定义的数据结构。
438
439**解决方案**
440
4411. 自定义Sendable类。[Sendable对象](arkts-sendable.md)可以在不同的子线程中共享,开发者可以将任务执行后的结果保存到Sendable对象上。
442
4432. TaskPool任务执行后的结果可以在.then中返回,需要保存的数据如果仅在当前线程使用,可以在.then中将执行结果保存到自定义的数据结构中。
444
445   ```ts
446   // sendable.ets,与Index.ets在同级目录下
447   @Sendable
448   export class testClass {
449     name: string = "test";
450     setName(name: string) {
451       this.name = name;
452     }
453     getName(): string {
454       return this.name;
455     }
456   }
457   ```
458
459   ```ts
460   // Index.ets
461   import { taskpool } from '@kit.ArkTS'
462   import { BusinessError } from '@kit.BasicServicesKit'
463   import { testClass } from './sendable'
464
465   @Concurrent
466   function createTask(a: number): string {
467     return `test${a}`;
468   }
469   function executeTask() {
470     let testObject: testClass = new testClass();
471     let task: taskpool.Task = new taskpool.Task(createTask, 1)
472     taskpool.execute(task).then((res) => {
473       testObject.setName(res as string);
474       console.info('execute task success, name is ' + testObject.getName());
475     }).catch((e: BusinessError) => {
476       console.error('execute task error: ' + e.message);
477     })
478   }
479   ```
480
481## Sendable类在子线程无法加载
482
483**问题描述**
484
485Sendable装饰器修饰的类与Observed装饰器修饰的类定义在同一个ets文件中,在TaskPool子线程加载Sendable类时捕获到错误信息:SendableItem is not initialized。
486
487```ts
488// Index.ets: 在Index页面新增以下代码
489import { taskpool } from '@kit.ArkTS'
490import { BusinessError } from '@kit.BasicServicesKit'
491import { SendableItem } from './sendable'
492
493@Concurrent
494function createTask() {
495  let data = new SendableItem();
496}
497
498function executeTask() {
499  let task = new taskpool.Task(createTask);
500  taskpool.execute(task).then((res) => {
501    console.info('execute task success');
502  }).catch((e: BusinessError) => {
503    console.error('execute task error: ' + e.message);
504  })
505}
506
507executeTask();
508```
509
510```ts
511// sendable.ets
512@Observed
513export class NormalItem {
514  age: number = 0;
515}
516
517@Sendable
518export class SendableItem {
519  name: string = '';
520}
521```
522
523**根因分析**
524
525Observed装饰器仅支持在UI线程使用,不能在子线程、Worker、TaskPool中直接或者间接使用,否则会导致应用功能失效甚至crash。由于sendable.ets文件中定义了Observed装饰器修饰的类,即使该类没有被显式调用也可能被解析执行,当解析到Observed这类UI装饰器时则抛出异常:Observed is not defined,导致当前文件中的其他模块的解析被中断。在TaskPool子线程加载Sendable类时抛出异常:SendableItem is not initialized。
526
527**解决方案**
528
529将Observed装饰器修饰的类NormalItem剥离到单独的ets文件后,TaskPool子线程再去加载Sendable类SendableItem,应用运行符合预期。
530
531```ts
532// Index.ets: 在Index页面新增以下代码
533import { taskpool } from '@kit.ArkTS'
534import { BusinessError } from '@kit.BasicServicesKit'
535import { SendableItem } from './sendable'
536
537@Concurrent
538function createTask() {
539  let data = new SendableItem();
540}
541
542function executeTask() {
543  let task = new taskpool.Task(createTask);
544  taskpool.execute(task).then((res) => {
545    console.info('execute task success');
546  }).catch((e: BusinessError) => {
547    console.error('execute task error: ' + e.message);
548  })
549}
550
551executeTask();
552```
553
554```ts
555// sendable.ets
556@Sendable
557export class SendableItem {
558  name: string = '';
559}
560```
561
562```ts
563// ui.ets
564@Observed
565export class NormalItem {
566  age: number = 0;
567}
568```
569