文档管理中心
我的
您当前正在浏览新版开发者文档中心,目录分类和层级有所调整。点击左侧当前文档分类名称前的“☰”图标,可切换文档分类。 了解新版目录
指南与API参考指南应用框架ArkTS(方舟编程语言)ArkTS并发应用多线程开发实践应用多线程开发实践案例C++线程间数据共享场景

C++线程间数据共享场景

在C++层进行多线程并发计算时,需要在每个C++线程上创建ArkTS执行环境,直接调用API。这样可以避免在非UI主线程回调时等待UI主线程的API调用结果。同时,还需要在C++线程之间共享和操作Sendable对象。

为了支持此类场景,C++线程需要能够创建并调用ArkTS,同时支持对Sendable对象进行多线程共享和操作。

在C++线程上调用ArkTS能力

使用Node-API接口在C++线程中创建ArkTS运行环境并调用的方法,可以参考使用Node-API接口创建ArkTS运行时环境。

核心代码片段如下所示:

ArkTS文件定义。

收起
自动换行
深色代码主题
复制
  1. @Sendable
  2. export class SendableObjTest {
  3. private value: number = 1024;
  4. static newSendable(): SendableObjTest {
  5. return new SendableObjTest();
  6. }
  7. public getValue(): number {
  8. return this.value;
  9. }
  10. }

实现Native加载ArkTS模块的能力。

收起
自动换行
深色代码主题
复制
  1. #include <thread>
  2. #include "napi/native_api.h"
  3. // 用于对比确认获取的sendable对象的value的值
  4. const int SENDABLE_VALUE = 1024;
  5. static void* g_serializationData = nullptr;
  6. static void* CreateEnvAndSendSendable(void*)
  7. {
  8. // 1. 创建基础运行环境
  9. napi_env env = nullptr;
  10. napi_status ret = napi_create_ark_runtime(&env);
  11. if (ret != napi_ok) {
  12. std::abort();
  13. }
  14. // 2. 加载自定义模块,假定SendableObjTest中提供创建sendable对象的方法newSendable
  15. napi_value test = nullptr;
  16. ret = napi_load_module_with_info(
  17. env, "entry/src/main/ets/pages/SendableObjTest", "com.example.myapplication/entry", &test);
  18. if (ret != napi_ok) {
  19. std::abort();
  20. }
  21. napi_value sendableObjTest = nullptr;
  22. ret = napi_get_named_property(env, test, "SendableObjTest", &sendableObjTest);
  23. if (ret != napi_ok) {
  24. std::abort();
  25. }
  26. // 3. 使用ArkTS中的newSendable,假设sendableObjTest中有一个函数newSendable能返回sendable对象
  27. napi_value newSendable = nullptr;
  28. ret = napi_get_named_property(env, sendableObjTest, "newSendable", &newSendable);
  29. if (ret != napi_ok) {
  30. std::abort();
  31. }
  32. // 4. 调用newSendable函数返回新创建的sendable对象,并保存在result中
  33. napi_value result = nullptr;
  34. ret = napi_call_function(env, sendableObjTest, newSendable, 0, nullptr, &result);
  35. if (ret != napi_ok) {
  36. std::abort();
  37. }
  38. // 5. 序列化sendable对象
  39. napi_value undefined;
  40. napi_get_undefined(env, &undefined);
  41. ret = napi_serialize(env, result, undefined, undefined, &g_serializationData);
  42. if (ret != napi_ok) {
  43. std::abort();
  44. }
  45. napi_destroy_ark_runtime(&env);
  46. return nullptr;
  47. }

主要步骤包括:创建执行环境、加载模块、查找并调用模块函数(或直接通过Node-API接口创建Sendable对象),最后销毁执行环境。加载模块的详细信息,请参见使用Node-API接口进行模块加载。查找并调用函数及更多Node-API接口能力,请参见Node-API。

在C++线程之间操作Sendable共享对象

在C++中调用ArkTS能力后,需要通过序列化和反序列化跨线程传递。napi_value不是多线程安全的,不能直接在多线程之间操作和共享。

下面代码例子说明了如何序列化和反序列化传递对象,注意因为Sendable共享对象是引用传递,所以序列化不会产生另外一份拷贝数据,而是直接传递对象引用到反序列化线程,所以在性能上相比非Sendable对象的序列化和反序列化更为高效。

ArkTS文件定义。

收起
自动换行
深色代码主题
复制
  1. @Sendable
  2. export class SendableObjTest {
  3. private value: number = 1024;
  4. static newSendable(): SendableObjTest {
  5. return new SendableObjTest();
  6. }
  7. public getValue(): number {
  8. return this.value;
  9. }
  10. }

在Native中实现两个线程的序列化和反序列化Sendable的逻辑。

收起
自动换行
深色代码主题
复制
  1. #include <thread>
  2. #include "napi/native_api.h"
  3. // 用于对比确认获取的sendable对象的value的值
  4. const int SENDABLE_VALUE = 1024;
  5. static void* g_serializationData = nullptr;
  6. static void* CreateEnvAndSendSendable(void*)
  7. {
  8. // 1. 创建基础运行环境
  9. napi_env env = nullptr;
  10. napi_status ret = napi_create_ark_runtime(&env);
  11. if (ret != napi_ok) {
  12. std::abort();
  13. }
  14. // 2. 加载自定义模块,假定SendableObjTest中提供创建sendable对象的方法newSendable
  15. napi_value test = nullptr;
  16. ret = napi_load_module_with_info(
  17. env, "entry/src/main/ets/pages/SendableObjTest", "com.example.myapplication/entry", &test);
  18. if (ret != napi_ok) {
  19. std::abort();
  20. }
  21. napi_value sendableObjTest = nullptr;
  22. ret = napi_get_named_property(env, test, "SendableObjTest", &sendableObjTest);
  23. if (ret != napi_ok) {
  24. std::abort();
  25. }
  26. // 3. 使用ArkTS中的newSendable,假设sendableObjTest中有一个函数newSendable能返回sendable对象
  27. napi_value newSendable = nullptr;
  28. ret = napi_get_named_property(env, sendableObjTest, "newSendable", &newSendable);
  29. if (ret != napi_ok) {
  30. std::abort();
  31. }
  32. // 4. 调用newSendable函数返回新创建的sendable对象,并保存在result中
  33. napi_value result = nullptr;
  34. ret = napi_call_function(env, sendableObjTest, newSendable, 0, nullptr, &result);
  35. if (ret != napi_ok) {
  36. std::abort();
  37. }
  38. // 5. 序列化sendable对象
  39. napi_value undefined;
  40. napi_get_undefined(env, &undefined);
  41. ret = napi_serialize(env, result, undefined, undefined, &g_serializationData);
  42. if (ret != napi_ok) {
  43. std::abort();
  44. }
  45. napi_destroy_ark_runtime(&env);
  46. return nullptr;
  47. }
  48. static void* CreateEnvAndReceiveSendable(void*)
  49. {
  50. // 1. 创建基础运行环境
  51. napi_env env = nullptr;
  52. napi_status ret = napi_create_ark_runtime(&env);
  53. if (ret != napi_ok) {
  54. std::abort();
  55. }
  56. // 2. 反序列化获取sendable共享对象,结果保存在result中,这个result就可以通过napi接口进行各种操作了
  57. napi_value result = nullptr;
  58. ret = napi_deserialize(env, g_serializationData, &result);
  59. if (ret != napi_ok) {
  60. std::abort();
  61. }
  62. // 3. 删除序列化数据
  63. ret = napi_delete_serialization_data(env, g_serializationData);
  64. if (ret != napi_ok) {
  65. std::abort();
  66. }
  67. // 4. 从 result 对象上获取 getValue 方法
  68. napi_value getValueFunc = nullptr;
  69. ret = napi_get_named_property(env, result, "getValue", &getValueFunc);
  70. if (ret != napi_ok) {
  71. std::abort();
  72. }
  73. // 5. 以 result 作为 this 调用 getValue 方法,返回值存入 valueResult
  74. napi_value valueResult = nullptr;
  75. ret = napi_call_function(env, result, getValueFunc, 0, nullptr, &valueResult);
  76. if (ret != napi_ok) {
  77. std::abort();
  78. }
  79. // 6. 确认valueResult为 number 类型
  80. napi_valuetype valuetype0;
  81. napi_typeof(env, valueResult, &valuetype0);
  82. if (valuetype0 != napi_number) {
  83. std::abort();
  84. }
  85. // 7. 1024是判断ArkTS返回的结果是否正确
  86. int value0;
  87. napi_get_value_int32(env, valueResult, &value0);
  88. if (value0 != SENDABLE_VALUE) {
  89. std::abort();
  90. }
  91. napi_destroy_ark_runtime(&env);
  92. return nullptr;
  93. }
  94. static napi_value TestSendSendable([[maybe_unused]] napi_env env, [[maybe_unused]] napi_callback_info info)
  95. {
  96. // UI 主线程创建 t1 线程
  97. std::thread t1(CreateEnvAndSendSendable, nullptr);
  98. t1.join();
  99. // UI 主线程创建 t2 线程
  100. std::thread t2(CreateEnvAndReceiveSendable, nullptr);
  101. t2.join();
  102. return nullptr;
  103. }
  104. EXTERN_C_START
  105. static napi_value Init(napi_env env, napi_value exports)
  106. {
  107. napi_property_descriptor desc[] = { { "testSendSendable", nullptr, TestSendSendable, nullptr, nullptr, nullptr,
  108. napi_default, nullptr } };
  109. napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
  110. return exports;
  111. }
  112. EXTERN_C_END
  113. static napi_module demoModule = {
  114. .nm_version = 1,
  115. .nm_flags = 0,
  116. .nm_filename = nullptr,
  117. .nm_register_func = Init,
  118. .nm_modname = "entry",
  119. .nm_priv = ((void*)0),
  120. .reserved = { 0 },
  121. };
  122. extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
  123. {
  124. napi_module_register(&demoModule);
  125. }
收起
自动换行
深色代码主题
复制
  1. export const testSendSendable: () => void;

UI主线程发起调用。

收起
自动换行
深色代码主题
复制
  1. import { hilog } from '@kit.PerformanceAnalysisKit';
  2. import testNapi from 'libentry.so';
  3. import { SendableObjTest } from './SendableObjTest'
  4. @Entry
  5. @Component
  6. struct Index {
  7. @State message: string = 'Hello World';
  8. build() {
  9. Row() {
  10. Column() {
  11. Text(this.message)
  12. .fontSize(50)
  13. .fontWeight(FontWeight.Bold)
  14. .onClick(() => {
  15. SendableObjTest.newSendable()
  16. hilog.info(0x0000, 'testTag', 'Test send Sendable begin');
  17. testNapi.testSendSendable();
  18. hilog.info(0x0000, 'testTag', 'Test send Sendable end');
  19. this.message = 'success';
  20. })
  21. }
  22. .width('100%')
  23. }
  24. .height('100%')
  25. }
  26. }

整个过程主要包括的逻辑实现为:

  1. UI主线程创建C++子线程t1,创建ArkTS运行环境并加载ArkTS模块,并创建Sendable对象,保存到result中,然后将result引用的Sendable对象序列化到全局序列化数据serializationData中。

  2. 当这些流程完成后,UI主线程创建C++子线程t2,并在这个新的线程中创建ArkTS运行环境。然后再通过反序列化接口从serializationData中反序列化出t1线程创建的Sendable对象,并保存到result中,从而实现了Sendable对象的跨C++线程传递。反序列化完成后,需要销毁序列化数据避免内存泄漏。

    说明

    操作对象需要符合Sendable对象的规则,具体可见Sendable使用规则与约束。