• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Function Flow Runtime任务伙伴(C++)
2
3## 概述
4
5在实际中,可能会遇到某一个线程(比如UI绘制线程)耗时很长,难以充分利用CPU多核能力的情况。此时,将原有线程的大任务拆分为多个小任务交给其他线程去执行是解决该问题的有效方法。但是,在实践中,会遇到两个阻碍:
6
7- 阻碍1:小任务中的部分片段受限于某些原因,必须执行在指定的线程上(比如访问JS虚拟机中的变量)。
8- 阻碍2:小任务会被拆分为多个小片段(可能由于阻碍1或者其他原因),单次执行时间可能在微秒级,而单次线程唤醒的开销在几十个微秒级,难以获得并发收益。
9
10这便是Job Partner并发范式产生的原因。
11
12![image](figures/ffrt_figure8.png)
13
14Job Partner中定义原先的线程为master线程,并支持动态管理partner线程(伙伴线程),它有两个特性:
15
16- 特性1:针对阻碍1,支持伙伴线程在执行任务过程中暂停当前任务的执行,之后将任务发送到master线程执行,直到master线程执行完成之后在partner线程继续执行原有任务。
17- 特性2:针对阻碍2,利用批量小任务的特征,动态将多个小任务合并为粒度更大的FFRT任务,以均摊单个小任务的调度开销。
18
19## 示例:某商城系统
20
21举例实现一个某商城系统的节点并行化创建,要求:节点创建的部分代码片段必须运行在js线程,其他片段可以在任意线程执行。
22
23原有伪代码实现:
24
25    ````cpp
26    namespace market_system {
27        // 批量创建节点,在js线程上被调用
28        void create_nodes(std::array<Node>& nodes)
29        {
30            for (int i = 0; i < nodes.size(); i++) {
31                code1(); // 可以在任意线程上执行的代码片段
32                code2(); // 必须在js线程上执行的代码片段
33                code3(); // 可以在任意线程上执行的代码片段
34            }
35        }
36    };
37    ````
38
39使用Job Partner并行化后的伪代码如下:
40
41    ````cpp
42    #include <array>
43    #include <memory>
44    #include "ffrt/ffrt.h"
45
46    namespace market_system {
47        // 批量创建节点,在js线程上被调用
48        void create_nodes(std::array<Node>& nodes)
49        {
50            constexpr uint64_t stack_size = 16 * 1024;
51            auto stack = std::make_unique<std::array<char, stack_size>[]>(nodes.size()); // 创建job_num个执行栈
52            auto partner = ffrt::job_partner<>::get_partner_of_this_thread(); // 获得当前js线程的伙伴
53            for (int i = 0; i < nodes.size(); i++) {
54                partner->submit([&] { // 每个节点的创建提交给partner
55                    code1(); // 可以在任意线程上执行的代码片段
56                    ffrt::job_partner<>::submit_to_master([&] { // 遇到必须在master线程执行的任务时发给主线程并同步等待
57                        code2(); // 必须在js线程上执行的代码片段
58                    });
59                    code3(); // 可以在任意线程上执行的代码片段
60                }, &stack[i], stack_size);
61            }
62            partner->wait(); // 等待所有节点创建完成
63        }
64    };
65    ````
66
67## 接口说明
68
69上述样例中涉及到主要的FFRT的接口包括:
70
71| 名称                                                           | 描述               |
72| -------------------------------------------------------------- | ------------------ |
73| [submit](ffrt-api-guideline-cpp.md#submit-suspendable-job)     | 提交可暂停的任务。 |
74| [wait](ffrt-api-guideline-cpp.md#wait)                         | 等待所有任务完成。 |
75| [submit_to_master](ffrt-api-guideline-cpp.md#submit_to_master) | 提交任务到主线程。 |
76
77> **说明:**
78>
79> - 如何使用FFRT C++ API详见:[FFRT C++接口三方库使用指导](ffrt-development-guideline.md#using-ffrt-c-api-1)。
80> - 使用FFRT C接口或C++接口时,都可以通过FFRT C++接口三方库简化头文件包含,即使用`#include "ffrt/ffrt.h"`头文件包含语句。
81
82## 约束限制
83
84- 适用Job Partner并发范式的场景应该是粒度较小且不会长时间阻塞线程的大批量任务。
85- 如果任务粒度较大(比如数百微秒以上),推荐使用FFRT更为通用的任务提交接口。
86- 如果任务不满足大批量的特征,使用Job Partner并发范式并不能显著降低调度开销。
87