• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Function Flow Runtime开发指导
2
3<!--Kit: Function Flow Runtime Kit-->
4<!--Subsystem: Resourceschedule-->
5<!--Owner: @chuchihtung; @yanleo-->
6<!--Designer: @geoffrey_guo; @huangyouzhong-->
7<!--Tester: @lotsof; @sunxuhao-->
8<!--Adviser: @foryourself-->
9
10## 介绍
11
12FFRT编程模型是一种基于任务和数据驱动的并发编程模型,允许开发者通过任务及其依赖关系描述的方式进行应用开发。
13通过FFRT编程模型,开发者可专注于应用功能开发,由FFRT在运行时根据任务依赖状态和可用执行资源自动并发调度和执行任务。
14
15本文用于指导开发者基于FFRT编程模型实现并行编程。
16
17## 维测
18
19### 超时监测
20
21FFRT提供开发者队列级和任务级超时维测机制,用来监控用户业务中承载重要职责的队列和任务在FFRT调度的端到端时间。
22
23- 当队列中任务发生超时,FFRT打印告警日志并通过回调接口通知到业务。
24- 当任务发生超时,FFRT打印告警日志并调用进程级回调函数。
25
26> **注意:**
27>
28> 任务超时时执行的回调函数进程范围内唯一,需要在任务提交之前由业务方配置到FFRT中,不支持在提交任务或任务超时检测过程中配置。
29
30具体接口包括:
31
32| C++接口                                                                                                                                   | C接口                                                                                | 描述                   |
33| ----------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ---------------------- |
34| [queue_attr::timeout](https://gitee.com/openharmony/resourceschedule_ffrt/blob/master/docs/ffrt-api-guideline-cpp.md#set-queue-timeout)   | [ffrt_queue_attr_set_timeout](ffrt-api-guideline-c.md#ffrt_queue_attr_t)   | 设置队列超时时间。     |
35| [queue_attr::callback](https://gitee.com/openharmony/resourceschedule_ffrt/blob/master/docs/ffrt-api-guideline-cpp.md#set-queue-callback) | [ffrt_queue_attr_set_callback](ffrt-api-guideline-c.md#ffrt_queue_attr_t) | 设置队列超时回调函数。 |
36
37### 长耗时任务监测
38
39**机制**
40
41- 长耗时任务打印机制 当任务执行时间超过一秒时,会触发一次堆栈打印,后续该任务堆栈打印频率调整为一分钟。连续打印十次后,打印频率调整为十分钟。再触发十次打印后,打印频率固定为三十分钟。
42- 该机制的堆栈打印调用的是DFX的`GetBacktraceStringByTid`接口,该接口会向阻塞线程发送抓栈信号,触发中断并抓取调用栈返回。
43
44**样例**
45
46在对应进程日志中搜索RecordSymbolAndBacktrace关键字,对应的日志示例如下:
47
48```txt
49W C01719/ffrt: 60500:RecordSymbolAndBacktrace:159 Tid[16579] function occupies worker for more than [1]s.
50W C01719/ffrt: 60501:RecordSymbolAndBacktrace:164 Backtrace:
51W C01719/ffrt: #00 pc 00000000000075f0 /system/lib64/module/file/libhash.z.so
52W C01719/ffrt: #01 pc 0000000000008758 /system/lib64/module/file/libhash.z.so
53W C01719/ffrt: #02 pc 0000000000012b98 /system/lib64/module/file/libhash.z.so
54W C01719/ffrt: #03 pc 000000000002aaa0 /system/lib64/platformsdk/libfilemgmt_libn.z.so
55W C01719/ffrt: #04 pc 0000000000054b2c /system/lib64/platformsdk/libace_napi.z.so
56W C01719/ffrt: #05 pc 00000000000133a8 /system/lib64/platformsdk/libuv.so
57W C01719/ffrt: #06 pc 00000000000461a0 /system/lib64/chipset-sdk/libffrt.so
58W C01719/ffrt: #07 pc 0000000000046d44 /system/lib64/chipset-sdk/libffrt.so
59W C01719/ffrt: #08 pc 0000000000046a6c /system/lib64/chipset-sdk/libffrt.so
60W C01719/ffrt: #09 pc 00000000000467b0 /system/lib64/chipset-sdk/libffrt.so
61```
62
63该维测会打印出Worker上执行时间超过阈值的任务堆栈、Worker线程号、执行时间,请自行根据堆栈找对应组件确认阻塞原因。
64
65**注意事项**
66
67不涉及。
68
69### 运行信息转储
70
71**机制**
72
73FFRT提供一个对外的接口`ffrt_dump`以便转储FFRT子系统运行时的内部信息,主要包含:
74
751. FFRT统计信息:提交任务数,运行任务数,协程切换次数,任务完成数;
762. Worker线程信息:每个QoS下的Worker数量,Worker id,当前运行的任务id,任务名以及任务类型;
773. 普通任务信息:当前进程中还未释放的普通任务,dump每个任务的任务名,id及调用栈信息;
784. 队列任务信息:当前进程中还未释放的队列任务,dump每个任务的任务名,id及调用栈信息。
79
80在当前进程发生freeze时,OH的DFX模块会主动调用`ffrt_dump`接口转储FFRT的信息,落盘到freeze文件中,存储到`/data/log/faultlog/faultlogger/`目录下,用户可以直接利用该文件中的任务调用栈信息定位对应任务的卡顿问题。
81
82**样例**
83
84```txt
85ready task ptr: qos 0 readptr 79 writeptr 79
86ready task ptr: qos 1 readptr 360 writeptr 360
87ready task ptr: qos 2 readptr 19 writeptr 19
88ready task ptr: qos 3 readptr 0 writeptr 0
89ready task ptr: qos 4 readptr 0 writeptr 0
90ready task ptr: qos 5 readptr 65 writeptr 65
91ready task ptr: qos 6 readptr 0 writeptr 0
92ready task ptr: qos 7 readptr 0 writeptr 0
93submit queue: readptr 24 writeptr 24
94intr wake: status 255
95proc status: taskCnt 23 vercnt 0sigCnt0
96    |-> worker count
97        qos 0: worker num:1 tid:31676
98        qos 2: worker num:3 tid:51349, 28769, 28565
99        qos 5: worker num:1 tid:30605
100    |-> worker status
101        qos 0: worker tid 31676 is running nothing
102        qos 2: worker tid 51349 is running nothing
103        qos 2: worker tid 28769 is running, task id 24591 name sq_CesSrvMain_12_PublishCommonEventDetailed_24591 fromTid 43928 createTime 2024-11-27 02:52:27.325248 executeTime 2024-11-27 02:52:27.326150
104        qos 2: worker tid 28565 is running, task id 24611 name sq_dfx_freeze_task_queue_16_NotifyAppFaultTask_24611 fromTid 43833 createTime 2024-11-27 02:52:38.114787 executeTime 2024-11-27 02:52:38.115424
105        qos 5: worker tid 30605 is running, task id 24595 name sq_AbilityManagerService_19_SubmitTaskInner_24595 fromTid 43610 createTime 2024-11-27 02:52:27.844237 executeTime 2024-11-27 02:52:27.844573
106    |-> ready queue status
107    |-> blocked by task dependence
108        <1/1>stack: task id 3,qos 2,name AgingTask fromTid 43417 createTime 2024-11-27 01:21:39.641673 executeTime 2024-11-27 01:21:39.642290
109#00 pc 0000000000065c5c /system/lib64/ndk/libffrt.so(CoYield()+560)(22be57f01a789a03813d26a19c3a4042)
110#01 pc 00000000000a3268 /system/lib64/ndk/libffrt.so(ffrt::this_task::SleepUntilImpl(std::__h::chrono::time_point<std::__h::chrono::steady_clock, std::__h::chrono::duration<long long, std::__h::ratio<1l, 1000000000l>>> const&)+356)(22be57f01a789a03813d26a19c3a4042)
111#02 pc 00000000000a39b4 /system/lib64/ndk/libffrt.so(ffrt_usleep+60)(22be57f01a789a03813d26a19c3a4042)
112#03 pc 0000000000420de0 /system/lib64/libbms.z.so(2eb52bd03af1b9a31e14ffe60bfc39da)
113#04 pc 00000000000a6a2c /system/lib64/ndk/libffrt.so(ffrt::CPUEUTask::Execute()+300)(22be57f01a789a03813d26a19c3a4042)
114#05 pc 0000000000066d18 /system/lib64/ndk/libffrt.so(22be57f01a789a03813d26a19c3a4042)
115```
116
117**注意事项**
118
119由于OH DFX模块在freeze时有处理时间的要求,存在较小概率会导致`ffrt_dump`中收集的信息不全,freeze处理时间耗尽,此时落盘的信息会有缺失。
120
121### 黑匣子日志
122
123**机制**
124
125进程Crash发生时,FFRT模块收到信号(`SIGABRT`、`SIGBUS`、`SIGFPE`、`SIGILL`、`SIGSTKFLT`、`SIGSTOP`、`SIGSYS`和`SIGTRAP`),将FFRT当前重要的运行时信息保存至faultlog中,包括:正在运行的task;当前Worker的运行信息和调用栈信息;当前普通任务信息;当前队列任务信息等。用户可利用这些信息中的内容辅助定位Crash问题。
126
127**样例**
128
129```txt
130C01719/CameraDaemon/ffrt: 9986:operator():254 <<<=== ffrt black box(BBOX) start ===>>>
131C01719/CameraDaemon/ffrt: 9987:SaveCurrent:63 <<<=== current status ===>>>
132C01719/CameraDaemon/ffrt: 9988:SaveCurrent:68 signal SIGABRT triggered: source tid 5962, task id 17, qos 2, name SvrWatchdog
133C01719/CameraDaemon/ffrt: 9989:SaveWorkerStatus:94 <<<=== worker status ===>>>
134C01719/CameraDaemon/ffrt: 9990:SaveWorkerStatus:100 qos 0: worker tid 6410 is running nothing
135C01719/CameraDaemon/ffrt: 9991:SaveWorkerStatus:100 qos 2: worker tid 5968 is running nothing
136C01719/CameraDaemon/ffrt: 9992:SaveWorkerStatus:100 qos 2: worker tid 5964 is running nothing
137C01719/CameraDaemon/ffrt: 9993:SaveWorkerStatus:100 qos 2: worker tid 5963 is running nothing
138C01719/CameraDaemon/ffrt: 9994:SaveWorkerStatus:105 qos 2: worker tid 5962 is running task id 17 name SvrWatchdog
139C01719/CameraDaemon/ffrt: 9995:SaveWorkerStatus:100 qos 2: worker tid 5967 is running nothing
140C01719/CameraDaemon/ffrt: 9996:SaveWorkerStatus:100 qos 2: worker tid 5965 is running nothing
141C01719/CameraDaemon/ffrt: 9997:SaveWorkerStatus:100 qos 2: worker tid 5961 is running nothing
142C01719/CameraDaemon/ffrt: 9998:SaveWorkerStatus:100 qos 2: worker tid 1146 is running nothing
143C01719/CameraDaemon/ffrt: 9999:SaveWorkerStatus:100 qos 2: worker tid 1145 is running nothing
144C01719/CameraDaemon/ffrt: 10000:SaveWorkerStatus:100 qos 2: worker tid 5966 is running nothing
145```
146
147**注意事项**
148
149不涉及。
150
151### Trace打点
152
153**机制**
154
155FFRT任务的调度和执行过程中,利用了OH系统的Trace打点能力,对任务在FFRT框架中的状态流转做了实时跟踪,用户可以借助Trace图形化工具来分析任务的行为是否符合预期。
156
157**样例**
158
1591. 启动Trace抓取
160
161    ```shell
162    hdc shell "hitrace -t 10 -b 20480 -o /data/local/tmp/in_systrace.ftrace sched freq idle ffrt"
163    # -t:指定trace采集时长,在采集过程中所有的trace记录会落盘保存
164    # -b:指定trace记录缓存大小,buffer不足的情况下可能导致部分记录被覆盖没有落盘
165    # -o:指定trace落盘存储路径
166    ```
167
1682. 图形化工具呈现
169
170    将Trace落盘的文件从设备中取出来,借助图形化工具进行分析,例如:[Perfetto](https://perfetto.dev/)171
172**注意事项**
173
174用户也可以在自己业务代码中加入Trace打点,以界定问题的范围。需注意在高频调用流程中,加入Trace打点会有系统开销,会对业务性能造成影响。
175
176### Debug日志
177
178**机制**
179
180- FFRT默认不开启Debug级别的日志,用户可以通过命令的方式打开,以获取更丰富的维测信息支撑开发阶段的问题定位。
181- 打开FFRT Debug日志开关:
182
183    ```shell
184    hdc shell hilog -b DEBUG -D 0xD001719
185    ```
186
187- 恢复默认FFRT INFO日志级别:
188
189    ```shell
190    hdc shell hilog -b INFO -D 0xD001719
191    ```
192
193**样例**
194
195```txt
1964190  5631 D C01719/neboard:EngineServiceAbility:1/ffrt: 275337:Detach:147 qos 3 thread not joinable
1973257  6075 D C01719/com.ohos.sceneboard/ffrt: 513070:SetDefaultThreadAttr:148 qos apply tid[6075] level[3]
198```
199
200**注意事项**
201
202由于FFRT是系统底座,支撑大量上层业务及框架的运行,全局打开Debug日志会导致日志超限,影响其他模块日志的正常输出。
203
204## 开发步骤
205
206以下步骤描述了如何使用FFRT提供的Native API接口,创建并行任务和串行队列任务以及销毁相应资源。
207
2081. 在项目`CMakeLists.txt`中添加动态链接库:
209
210    ```txt
211    libffrt.z.so
212    ```
213
2142. 在项目中包含对应的头文件:
215
216    ```cpp
217    #include "ffrt/task.h"
218    #include "ffrt/mutex.h"
219    #include "ffrt/shared_mutex.h"
220    #include "ffrt/condition_variable.h"
221    #include "ffrt/sleep.h"
222    #include "ffrt/queue.h"
223    #include "ffrt/loop.h"
224    #include "ffrt/timer.h"
225    ```
226
2273. 对执行的函数进行封装:
228
229    ```cpp
230    // 第一种使用模板,支持C++
231    template<class T>
232    struct function {
233        ffrt_function_header_t header;
234        T closure;
235    };
236
237    template<class T>
238    void exec_function_wrapper(void* t)
239    {
240        auto f = reinterpret_cast<function<std::decay_t<T>>*>(t);
241        f->closure();
242    }
243
244    template<class T>
245    void destroy_function_wrapper(void* t)
246    {
247        auto f = reinterpret_cast<function<std::decay_t<T>>*>(t);
248        f->closure = nullptr;
249    }
250
251    template<class T>
252    inline ffrt_function_header_t* create_function_wrapper(T&& func,
253        ffrt_function_kind_t kind = ffrt_function_kind_general)
254    {
255        using function_type = function<std::decay_t<T>>;
256        static_assert(sizeof(function_type) <= ffrt_auto_managed_function_storage_size,
257            "size of function must be less than ffrt_auto_managed_function_storage_size");
258
259        auto p = ffrt_alloc_auto_managed_function_storage_base(kind);
260        auto f = new (p)function_type;
261        f->header.exec = exec_function_wrapper<T>;
262        f->header.destroy = destroy_function_wrapper<T>;
263        f->closure = std::forward<T>(func);
264        return reinterpret_cast<ffrt_function_header_t*>(f);
265    }
266
267    // 第二种创建方式
268    typedef struct {
269        ffrt_function_header_t header;
270        ffrt_function_t func;
271        ffrt_function_t after_func;
272        void* arg;
273    } ffrt_function_wrapper_t;
274
275    static inline void ffrt_exec_function_wrapper(void* t)
276    {
277       ffrt_function_wrapper_t* f = (ffrt_function_wrapper_t *)t;
278       if (f->func) {
279           f->func(f->arg);
280       }
281    }
282
283    static inline void ffrt_destroy_function_wrapper(void* t)
284    {
285        ffrt_function_wrapper_t* f = (ffrt_function_wrapper_t *)t;
286        if (f->after_func) {
287            f->after_func(f->arg);
288        }
289    }
290
291    #define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
292    static inline ffrt_function_header_t *ffrt_create_function_wrapper(ffrt_function_t func,
293        ffrt_function_t after_func, void* arg, ffrt_function_kind_t kind)
294    {
295        FFRT_STATIC_ASSERT(sizeof(ffrt_function_wrapper_t) <= ffrt_auto_managed_function_storage_size,
296            size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
297
298        ffrt_function_wrapper_t* f = (ffrt_function_wrapper_t *)ffrt_alloc_auto_managed_function_storage_base(kind);
299        f->header.exec = ffrt_exec_function_wrapper;
300        f->header.destroy = ffrt_destroy_function_wrapper;
301        f->func = func;
302        f->after_func = after_func;
303        f->arg = arg;
304        return (ffrt_function_header_t *)f;
305    }
306
307    // 样例:待提交执行的函数
308    void OnePlusForTest(void* arg)
309    {
310        (*static_cast<int*>(arg)) += 1;
311    }
312    ```
313
3144. 设置任务属性值,包括QoS等级、任务名称等。
315
316    ```cpp
317    // ******初始化并行任务属性******
318    ffrt_task_attr_t attr;
319    ffrt_task_attr_init(&attr);
320
321    // ******创建串行队列******
322
323    // 创建串行队列的属性
324    ffrt_queue_attr_t queue_attr;
325    // 创建串行队列的handle
326    ffrt_queue_t queue_handle;
327
328    // 初始化队列属性
329    (void)ffrt_queue_attr_init(&queue_attr);
330
331    // 如有需要,设置指定QoS等级
332    ffrt_queue_attr_set_qos(&queue_attr, static_cast<ffrt_qos_t>(ffrt_qos_inherit));
333    // 如有需要,设置超时时间(ms)
334    ffrt_queue_attr_set_timeout(&queue_attr, 10000);
335    // 如有需要,设置超时回调
336    int x = 0;
337    ffrt_queue_attr_set_callback(&queue_attr, ffrt_create_function_wrapper(OnePlusForTest, NULL, &x,
338        ffrt_function_kind_queue));
339
340    // 基于属性,初始化队列
341    queue_handle = ffrt_queue_create(ffrt_queue_serial, "test_queue", &queue_attr);
342    ```
343
3445. 提交任务。
345
346    ```cpp
347    int a = 0;
348    // ******并行任务******
349    // 提交不带handle返回值的并行任务
350    ffrt_submit_base(
351        ffrt_create_function_wrapper(OnePlusForTest, NULL, &a, ffrt_function_kind_general), NULL, NULL, &attr);
352    // 提交带handle返回值的并行任务
353    ffrt_task_handle_t task = ffrt_submit_h_base(
354        ffrt_create_function_wrapper(OnePlusForTest, NULL, &a, ffrt_function_kind_general), NULL, NULL, &attr);
355
356    // ******串行任务******
357    // 提交不返回handle的串行队列任务
358    ffrt_queue_submit(queue_handle,
359        ffrt_create_function_wrapper(OnePlusForTest, NULL, &a, ffrt_function_kind_queue), NULL);
360    // 提交带handle的串行队列任务
361    ffrt_task_handle_t handle = ffrt_queue_submit_h(queue_handle,
362        ffrt_create_function_wrapper(OnePlusForTest, NULL, &a, ffrt_function_kind_queue), NULL);
363
364    // 如果需要等待执行结果,则调用wait
365    const std::vector<ffrt_dependence_t> wait_deps = {{ffrt_dependence_task, task}};
366    ffrt_deps_t wait{static_cast<uint32_t>(wait_deps.size()), wait_deps.data()};
367    ffrt_wait_deps(&wait);
368
369    ffrt_queue_wait(handle);
370    ```
371
3726. 在任务不需要任何销毁动作时可以通过简化接口提交任务(可选)。
373
374    ```cpp
375    int a = 0;
376    // 在步骤3场景中的after_func函数指针为NULL时,可以使用简化接口提交任务,避免冗余的任务结构封装。
377    // ******并行任务******
378    // 通过简化接口提交不带handle返回值的并行任务
379    ffrt_submit_f(OnePlusForTest, &a, NULL, NULL, &attr);
380    // 通过简化接口提交带handle返回值的并行任务
381    ffrt_task_handle_t task = ffrt_submit_h_f(OnePlusForTest, &a, NULL, NULL, &attr);
382
383    // ******串行任务******
384    // 通过简化接口提交不返回handle的串行队列任务
385    ffrt_queue_submit_f(queue_handle, OnePlusForTest, &a, NULL);
386    // 通过简化接口提交带handle的串行队列任务
387    ffrt_task_handle_t handle = ffrt_queue_submit_h_f(queue_handle, OnePlusForTest, &a, NULL);
388
389    // 如果需要等待执行结果,则调用wait
390    const std::vector<ffrt_dependence_t> wait_deps = {{ffrt_dependence_task, task}};
391    ffrt_deps_t wait{static_cast<uint32_t>(wait_deps.size()), wait_deps.data()};
392    ffrt_wait_deps(&wait);
393
394    ffrt_queue_wait(handle);
395    ```
396
3977. 任务提交完成后销毁相应资源。
398
399    ```cpp
400    // ******销毁并行任务******
401    ffrt_task_attr_destroy(&attr);
402    ffrt_task_handle_destroy(task);
403
404    // ******销毁串行队列任务******
405    // 先销毁任务handle,再销毁队列
406    ffrt_queue_attr_destroy(&queue_attr);
407    ffrt_task_handle_destroy(handle);
408    ffrt_queue_destroy(queue_handle);
409    ```
410
411## 使用建议
412
413### 建议1:函数化
414
415- 程序过程各步骤以函数封装表达,函数满足类纯函数特性。
416- 无全局数据访问。
417- 无内部状态保留。
418- 通过`ffrt_submit_base()`或`ffrt_submit_f()`接口以异步任务方式提交函数执行。
419- 将函数访问的数据对象以及访问方式在`ffrt_submit_base()`接口中的`in_deps`和`out_deps`参数表达。
420- 程序员通过`in_deps`和`out_deps`参数表达任务间依赖关系以保证程序执行的正确性。
421
422> 做到纯函数的好处在于:1. 能够最大化挖掘并行度,2.避免DataRace和锁的问题。
423>
424> 在实际中,可以根据场景放松纯函数的约束,但前提是:
425>
426> - 确定添加的`in_deps`和`out_deps`可确保程序正确执行。
427> - 通过FFRT提供的锁机制保护对全局变量的访问。
428
429### 建议2:使用FFRT提供的替代API
430
431- 禁止在FFRT任务中使用系统线程库API创建线程,使用`submit`提交任务。
432- 使用FFRT提供的锁,条件变量,睡眠,IO等API代替系统线程库API。
433  - 使用系统线程库API可能造成工作线程阻塞,引起额外性能开销。
434
435### 建议3:Deadline机制
436
437- 必须用于具备周期/重复执行特征的处理流程。
438- 在有明确时间约束和性能关键的处理流程中使用,避免滥用。
439- 在相对大颗粒度的处理流程中使用,例如具有16.6ms时间约束的帧处理流程。
440
441### 建议4:从线程模型迁移
442
443- 创建线程替代为创建FFRT任务。
444  - 线程从逻辑上类似无`in_deps`的任务。
445- 识别线程间的依赖关系,并将其表达在任务的依赖关系`in_deps`和`out_deps`上。
446- 线程内计算过程分解为异步任务调用。
447- 通过任务依赖关系和锁机制避免并发任务数据竞争问题。
448
449### 建议5:推荐使用C++接口
450
451- FFRT的C++接口是基于C接口实现,在使用API接口时可以手动添加C++相关头文件后配套使用。
452
453## 约束限制
454
455### 线程局部变量使用约束
456
457FFRT Task中使用线程局部变量存在风险,说明如下:
458
459- 线程局部变量包括C/C++语言提供的`thread_local`定义的变量和使用`pthread_key_create`创建的变量。
460- FFRT支持任务调度,任务被调度到任意可用线程执行,进而使用线程局部变量是有风险的,这一点和所有其他支持任务并发调度的框架一致。
461- FFRT的任务默认以协程的方式运行,任务执行过程中可能发生协程退出,恢复执行时,执行该任务的线程可能发生变更。
462
463### 线程绑定使用约束
464
465- FFRT支持任务调度,任务调度到哪个线程是随机的,thread_idx/线程优先级/线程亲和性等与线程绑定的行为禁止在任务中使用。
466
467### 标准库同步原语使用约束
468
469FFRT任务中使用标准库的递归互斥锁可能发生死锁,需要更换为FFRT提供的递归互斥锁,说明如下:
470
471- 递归互斥锁在`lock()`成功时记录调用者“执行栈”作为锁的所有者,在后续`lock()`时会判断调用者是否为当前执行栈,如果是则返回成功,以支持在同一个执行栈中嵌套获取锁。在标准库的实现中,“执行栈”以线程标识表示。
472- 在FFRT任务中使用标准库的递归互斥锁,如果在外层和内层`lock()`之间,发生任务(协程)退出,任务恢复执行时在不同于首次调用`lock()`的FFRT Worker线程上,则判断当前线程不是所有者,`lock()`失败,FFRT Worker线程被挂起,后面的`unlock()`不会被执行,从而出现死锁。
473
474### 对进程`fork()`场景的支持说明
475
476- 在未使用FFRT的进程中,创建子进程,支持在该子进程中使用FFRT。
477- 在已经使用FFRT的进程中,单独以`fork()`方式创建子进程,不支持在该子进程中使用FFRT。
478- 在已经使用FFRT的进程中,同时以`fork()`和`exec()`方式创建子进程,支持在子进程中使用FFRT。
479
480### 以动态库方式部署FFRT
481
482- 只能以动态库方式部署FFRT,静态库部署可能有多实例问题,例如:当多个被同一进程加载的so都以静态库的方式使用FFRT时,FFRT会被实例化成多份,其行为是未知的,这也不是FFRT设计的初衷。
483
484### 输入输出依赖数量的限制
485
486- 使用`ffrt_submit_base`接口进行任务提交时,每个任务的输入依赖和输出依赖的数量之和不能超过8个。
487- 使用`ffrt_submit_h_base`接口进行任务提交时,每个任务的输入依赖和输出依赖的数量之和不能超过7个。
488- 参数既作为输入依赖又作为输出依赖的时候,统计依赖数量时只统计一次,如输入依赖是`{&x}`,输出依赖也是`{&x}`,实际依赖的数量是1。
489
490### 进程或者线程退出时的限制
491
492- 进程退出时,FFRT内部的线程池等进程内共享的资源已经释放,禁止调用FFRT任务提交等接口。
493- 线程退出时,FFRT内部的thread local资源已经释放,正在退出的线程禁止调用FFRT任务提交等接口。
494
495## 常见反模式
496
497### C API中初始化FFRT对象后,对象的置空与销毁由用户负责
498
499- 为保证较高的性能,FFRT的C API中内部不包含对对象的销毁状态的标记,用户需要合理地进行资源的释放,重复调用各个对象的销毁操作,其结果是未定义的。
500- 错误示例1,重复调用销毁函数可能造成不可预知的数据损坏:
501
502    ```cpp
503    #include <stdio.h>
504    #include "ffrt/cpp/task.h"
505
506    void abnormal_case_1()
507    {
508        ffrt_task_handle_t h = ffrt_submit_h_base(
509            ffrt::create_function_wrapper(std::function<void()>([](){ printf("Test task running...\n"); })),
510            NULL, NULL, NULL);
511        // ...
512        ffrt_task_handle_destroy(h);
513        ffrt_task_handle_destroy(h); // 重复释放
514    }
515    ```
516
517- 错误示例2,未调用销毁函数会造成内存泄漏:
518
519    ```cpp
520    #include <stdio.h>
521    #include "ffrt/cpp/task.h"
522
523    void abnormal_case_2()
524    {
525        ffrt_task_handle_t h = ffrt_submit_h_base(
526            ffrt::create_function_wrapper(std::function<void()>([](){ printf("Test task running...\n"); })),
527            NULL, NULL, NULL);
528        // ...
529        // 内存泄露
530    }
531    ```
532
533- 建议示例,仅调用一次销毁函数,如有必要可进行置空:
534
535    ```cpp
536    #include <stdio.h>
537    #include "ffrt/cpp/task.h"
538
539    void normal_case()
540    {
541        ffrt_task_handle_t h = ffrt_submit_h_base(
542            ffrt::create_function_wrapper(std::function<void()>([](){ printf("Test task running...\n"); })),
543            NULL, NULL, NULL);
544        // ...
545        ffrt_task_handle_destroy(h);
546        h = nullptr; // 必要时置空任务句柄变量
547    }
548    ```
549
550### 变量生命周期错误
551
552- FFRT提交任务中应注意对象或资源在其生命周期内可能出现的误用,这些错误会导致程序崩溃、数据损坏或者难以调试的问题。
553- 错误示例1,变量生命周期已结束导致的UAF问题:
554
555    ```cpp
556    #include <unistd.h>
557    #include "ffrt/cpp/task.h"
558
559    void abnormal_case_3()
560    {
561        int x = 0;
562        ffrt::submit([&] {
563            usleep(1000); // 模拟业务处理逻辑
564            x++;          // 此时变量生命周期可能已结束,对变量的访问会发生UAF问题
565        });
566    }
567    ```
568
569- 错误示例2,互斥锁生命周期已结束继续使用导致功能异常:
570
571    ```cpp
572    #include <unistd.h>
573    #include "ffrt/cpp/mutex.h"
574    #include "ffrt/cpp/task.h"
575
576    void abnormal_case_4()
577    {
578        ffrt::mutex lock;
579        ffrt::submit([&] {
580            lock.lock();   // 对FFRT锁进行操作时,要保证其生命周期
581            usleep(1000);  // 模拟业务处理逻辑
582            lock.unlock(); // 对FFRT锁进行操作时,要保证其生命周期
583        });
584    }
585    ```
586
587## Using FFRT in DevEco Studio
588
589### Using FFRT C API
590
591NDK(Native Development Kit)是系统提供的Native API的集合,方便开发者使用C或C++语言实现应用的关键功能。
592
593FFRT C API已集成在NDK中,在DevEco Studio中可以直接使用对应的接口。
594
595```c
596#include "ffrt/task.h"
597#include "ffrt/mutex.h"
598#include "ffrt/shared_mutex.h"
599#include "ffrt/condition_variable.h"
600#include "ffrt/sleep.h"
601#include "ffrt/queue.h"
602#include "ffrt/loop.h"
603#include "ffrt/timer.h"
604#include "ffrt/type_def.h"
605```
606
607### Using FFRT C++ API
608
609FFRT的部署依赖FFRT动态库`libffrt.so`和一组头文件,其中动态库仅导出C接口,C++接口调用C接口,并基于头文件的方式将API中的C++元素编译到用户的动态库中,从而保证了ABI兼容性。
610
611![image](figures/ffrt_figure7.png)
612
613如果要使用FFRT C++ API,需要使用FFRT C++接口三方库[@ppd/ffrt](https://ohpm.openharmony.cn/#/cn/detail/@ppd%2Fffrt),该三方库是由FFRT官方维护的FFRT C++ API库。
614
615在模块目录下执行三方库安装命令:
616
617```shell
618ohpm install @ppd/ffrt
619```
620
621也可以直接在`oh-package.json5`文件中配置对应的依赖,由DevEco Studio自动进行三方库下载安装。
622
623在模块`CMakeLists.txt`文件中添加头文件搜索路径和链接依赖:
624
625```cmake
626# ${MODULES_PATH}表示三方库安装位置,需要开发者自己定义或者直接替换成绝对路径或者相对路径。
627include_directories(${MODULES_PATH}/@ppd/ffrt/include)
628target_link_libraries(${TARGET_NAME} PUBLIC libffrt.z.so)
629```
630
631至此就可以在代码中使用FFRT C++接口:
632
633```cpp
634// include all C or C++ header files
635#include "ffrt/ffrt.h"
636
637// include specified header files
638#include "ffrt/cpp/task.h"
639#include "ffrt/cpp/mutex.h"
640#include "ffrt/cpp/shared_mutex.h"
641#include "ffrt/cpp/condition_variable.h"
642#include "ffrt/cpp/sleep.h"
643#include "ffrt/cpp/queue.h"
644```
645