1# C++线程间数据共享场景
2
3当应用在C++层进行多线程的并发计算时,因为ArkTS的API需要在ArkTS的环境执行,为了避免在非UI主线程每次回调等待UI主线程ArkTS环境中执行的API调用结果,需要在这些C++线程上创建ArkTS执行环境和直接调用API,并且可能需要在C++线程之间对Sendable对象进行共享和操作。
4
5为了支持此类场景,需要实现在C++线程上创建调用ArkTS的能力,其次还需要对Sendable对象进行多线程共享和操作。
6
7
8## 在C++线程上调用ArkTS能力
9
10关于如何使用Node-API接口在C++线程创建ArkTS运行环境并调用,具体请见[使用Node-API接口创建ArkTS运行时环境](../napi/use-napi-ark-runtime.md)。
11
12核心代码片段如下所示:
13
14ArkTS文件定义
15
16```ts
17// SendableObjTest.ets
18@Sendable
19export class SendableObjTest {
20  static newSendable() {
21    return 1024;
22  }
23}
24```
25
26
27Naitve实现加载ArkTS模块的能力
28
29```cpp
30// napi_init.cpp
31#include "napi/native_api.h"
32#include <thread>
33static void *CreateArkRuntimeFunc(void *arg)
34{
35    // 1. 创建基础运行环境
36    napi_env env = nullptr;
37    napi_status ret = napi_create_ark_runtime(&env);
38    if (ret != napi_ok) {
39        std::abort();
40    }
41    // 2. 加载自定义模块,假定SendableObjTest中提供创建sendable对象的方法newSendable
42    napi_value test = nullptr;
43    ret = napi_load_module_with_info(env, "entry/src/main/ets/pages/SendableObjTest", "com.example.myapplication/entry", &test);
44    if (ret != napi_ok) {
45        std::abort();
46    }
47    napi_value sendableObjTest = nullptr;
48    ret = napi_get_named_property(env, test, "SendableObjTest", &sendableObjTest);
49    if (ret != napi_ok) {
50        std::abort();
51    }
52    // 3. 使用ArkTS中的newSendable,假设sendableObjTest中有一个函数newSendable能返回sendable对象
53    napi_value newSendable = nullptr;
54    ret = napi_get_named_property(env, sendableObjTest, "newSendable", &newSendable);
55    if (ret != napi_ok) {
56        std::abort();
57    }
58    // 4. 调用newSendable函数返回新创建的sendable对象,并保存在result中
59    napi_value result = nullptr;
60    ret = napi_call_function(env, sendableObjTest, newSendable, 0, nullptr, &result);
61    if (ret != napi_ok) {
62        std::abort();
63    }
64    // 5. 获取ArkTS返回的结果
65    int value0;
66    napi_get_value_int32(env, result, &value0);
67    if (value0 != 1024) {
68        std::abort();
69    }
70    // 6. 销毁ArkTS环境
71    ret = napi_destroy_ark_runtime(&env);
72    return nullptr;
73}
74```
75
76主要分为四个步骤:创建执行环境、加载模块、查找并调用模块(也可以直接通过Node-API接口创建Sendable对象)的函数和最后销毁执行环境。其中第二步加载模块具体可见[使用Node-API接口进行模块加载](../napi/use-napi-load-module-with-info.md),第三步查找并调用函数及更多Node-API接口能力可见[Node-API](../reference/native-lib/napi.md#node-api)。
77
78## 在C++线程之间操作Sendable共享对象
79
80实现在C++调用ArkTS能力之后,需要通过序列化和反序列化跨线程传递。因为napi_value不是多线程安全的,所以不能直接在多线程之间直接共享napi_value变量。
81
82下面代码例子说明了如何序列化和反序列化传递对象,注意因为Sendable共享对象是引用传递,所以序列化不会产生另外一份拷贝数据,而是直接传递对象引用到反序列化线程,所以在性能上相比非Sendable对象的序列化和反序列化更为高效。
83
84ArkTS文件定义
85
86```ts
87// SendableObjTest.ets
88@Sendable
89export class SendableObjTest {
90  static newSendable() {
91    return 1024;
92  }
93}
94```
95
96Naitve实现两个线程的序列化反序列化Sendable的逻辑
97
98```cpp
99// napi_init.cpp
100#include "napi/native_api.h"
101#include <thread>
102
103static void *serializationData = nullptr;
104static void *CreateEnvAndSendSendable(void *) {
105    // 1. 创建基础运行环境
106    napi_env env = nullptr;
107    napi_status ret = napi_create_ark_runtime(&env);
108    if (ret != napi_ok) {
109        std::abort();
110    }
111    // 2. 加载自定义模块,假定SendableObjTest中提供创建sendable对象的方法newSendable
112    napi_value test = nullptr;
113    ret = napi_load_module_with_info(env, "entry/src/main/ets/pages/SendableObjTest", "com.example.myapplication/entry",
114                                     &test);
115    if (ret != napi_ok) {
116        std::abort();
117    }
118    napi_value sendableObjTest = nullptr;
119    ret = napi_get_named_property(env, test, "SendableObjTest", &sendableObjTest);
120    if (ret != napi_ok) {
121        std::abort();
122    }
123    // 3. 使用ArkTS中的newSendable,假设sendableObjTest中有一个函数newSendable能返回sendable对象
124    napi_value newSendable = nullptr;
125    ret = napi_get_named_property(env, sendableObjTest, "newSendable", &newSendable);
126    if (ret != napi_ok) {
127        std::abort();
128    }
129    // 4. 调用newSendable函数返回新创建的sendable对象,并保存在result中
130    napi_value result = nullptr;
131    ret = napi_call_function(env, sendableObjTest, newSendable, 0, nullptr, &result);
132    if (ret != napi_ok) {
133        std::abort();
134    }
135    // 5. 序列化sendable对象
136    napi_value undefined;
137    napi_get_undefined(env, &undefined);
138    ret = napi_serialize(env, result, undefined, undefined, &serializationData);
139    if (ret != napi_ok) {
140        std::abort();
141    }
142    return nullptr;
143}
144
145static void *CreateEnvAndReceiveSendable(void *) {
146    // 1. 创建基础运行环境
147    napi_env env = nullptr;
148    napi_status ret = napi_create_ark_runtime(&env);
149    if (ret != napi_ok) {
150        std::abort();
151    }
152    // 2. 反序列化获取sendable共享对象,结果保存在result中,这个result就可以通过napi接口进行各种操作了
153    napi_value result = nullptr;
154    ret = napi_deserialize(env, serializationData, &result);
155    if (ret != napi_ok) {
156        std::abort();
157    }
158    // 3. 删除序列化数据
159    ret = napi_delete_serialization_data(env, serializationData);
160    if (ret != napi_ok) {
161        std::abort();
162    }
163    napi_valuetype valuetype0;
164    napi_typeof(env, result, &valuetype0);
165    if (valuetype0 != napi_number) {
166        std::abort();
167    }
168    int value0;
169    napi_get_value_int32(env, result, &value0);
170    if (value0 != 1024) {
171        std::abort();
172    }
173    return nullptr;
174}
175
176static napi_value TestSendSendable([[maybe_unused]] napi_env env, [[maybe_unused]] napi_callback_info info) {
177    std::thread t1(CreateEnvAndSendSendable, nullptr);
178    t1.join();
179    std::thread t2(CreateEnvAndReceiveSendable, nullptr);
180    t2.join();
181    return nullptr;
182}
183
184EXTERN_C_START
185static napi_value Init(napi_env env, napi_value exports) {
186    napi_property_descriptor desc[] = {
187        {"testSendSendable", nullptr, TestSendSendable, nullptr, nullptr, nullptr, napi_default, nullptr}};
188    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
189    return exports;
190}
191EXTERN_C_END
192
193static napi_module demoModule = {
194    .nm_version = 1,
195    .nm_flags = 0,
196    .nm_filename = nullptr,
197    .nm_register_func = Init,
198    .nm_modname = "entry",
199    .nm_priv = ((void *)0),
200    .reserved = {0},
201};
202
203extern "C" __attribute__((constructor)) void RegisterEntryModule(void) {
204    napi_module_register(&demoModule);
205}
206```
207
208
209```
210// Index.d.ts
211export const testSendSendable: () => void;
212```
213
214UI主线程发起调用
215
216```ts
217// Index.ets
218import { hilog } from '@kit.PerformanceAnalysisKit';
219import testNapi from 'libentry.so';
220import { SendableObjTest } from './SendableObjTest'
221
222@Entry
223@Component
224struct Index {
225  @State message: string = 'Hello World';
226
227  build() {
228    Row() {
229      Column() {
230        Text(this.message)
231          .fontSize(50)
232          .fontWeight(FontWeight.Bold)
233          .onClick(() => {
234            SendableObjTest.newSendable()
235            hilog.info(0x0000, 'testTag', 'Test send Sendable begin');
236            testNapi.testSendSendable();
237            hilog.info(0x0000, 'testTag', 'Test send Sendable end');
238          })
239      }
240      .width('100%')
241    }
242    .height('100%')
243  }
244}
245```
246
247整个过程主要包括的逻辑实现为:
248
2491. 在入口main函数所在的UI主线程创建ArkTS运行环境,并发起一个C++子线程创建Sendable对象保存到result中,然后将result引用的Sendable对象序列化到一个全局序列化数据serializationData中。
250
2512. 当这些流程完成后,发起另外一个C++子线程,并在这个新的线程中创建ArkTS运行环境。然后再通过反序列化接口从serializationData中反序列化出UI主线程创建的Sendable对象,并保存到result中,从而实现了Sendable对象的跨C++线程传递。反序列化完成后,需要销毁反序列化数据避免内存泄露。这时UI主线程和子线程都同时持有这个Sendable共享对象,即可通过Node-API进行对象操作,比如读写或者传递到ArkTS层等。
252
253   > **说明:**
254   >
255   > 操作对象需要符合Sendable对象的规则,具体可见[Sendable使用规则与约束](sendable-constraints.md)。