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)。