• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 使用Node-API接口进行线程安全开发
2<!--Kit: NDK-->
3<!--Subsystem: arkcompiler-->
4<!--Owner: @xliu-huanwei; @shilei123; @huanghello-->
5<!--Designer: @shilei123-->
6<!--Tester: @kirl75; @zsw_zhushiwei-->
7<!--Adviser: @fang-jinxu-->
8
9
10## 场景介绍
11
12napi_create_threadsafe_function是Node-API接口之一,用于创建一个线程安全的JavaScript函数。该函数主要用于在多个线程之间共享和调用,避免竞争条件和死锁。包含以下场景:
13
14
15- 异步计算:若需执行耗时的计算或IO操作,可创建线程安全的函数,在另一线程中完成计算或IO操作,避免阻塞主线程,提升程序响应速度。
16
17- 数据共享:若多个线程需访问同一份数据,可以创建一个线程安全的函数,避免数据进行读写操作时发生竞争条件或死锁等问题。
18
19- 多线程编程:若需要进行多线程编程,可以创建一个线程安全的函数,确保多个线程之间的通信和同步操作正确。
20
21
22## 使用示例
23
241. 定义线程安全函数在Native入口。
25   ```c++
26   #include "napi/native_api.h"
27   #include "hilog/log.h"
28   #include <future>
29
30   struct CallbackData {
31       napi_threadsafe_function tsfn;
32       napi_async_work work;
33   };
34
35   static napi_value StartThread(napi_env env, napi_callback_info info)
36   {
37       size_t argc = 1;
38       napi_value jsCb = nullptr;
39       CallbackData *callbackData = new CallbackData(); // 异步任务完成时释放
40       napi_get_cb_info(env, info, &argc, &jsCb, nullptr, nullptr);
41
42       // 创建一个线程安全函数
43       napi_value resourceName = nullptr;
44       napi_create_string_utf8(env, "Thread-safe Function Demo", NAPI_AUTO_LENGTH, &resourceName);
45       napi_create_threadsafe_function(env, jsCb, nullptr, resourceName, 0, 1, nullptr, nullptr,
46           nullptr, CallJs, &callbackData->tsfn);
47
48       // 创建一个异步任务
49       // ExecuteWork会执行在一个由libuv创建的非JS线程上,此处使用napi_create_async_work是为了模拟在非JS线程场景使用napi_call_threadsafe_function接口向JS线程提交任务
50       napi_create_async_work(env, nullptr, resourceName, ExecuteWork, WorkComplete, callbackData,
51           &callbackData->work);
52
53       // 将异步任务加入到异步队列中
54       napi_queue_async_work(env, callbackData->work);
55       return nullptr;
56   }
57   ```
58   <!-- @[napi_thread_safety_cpp](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/NodeAPI/NodeAPIClassicUseCases/NodeAPIApplicationScenario/entry/src/main/cpp/thread_safety.cpp) -->
59
602. 在工作线程中调用ExecuteWork并执行线程安全函数。
61   ```c++
62   static void ExecuteWork(napi_env env, void *data)
63   {
64       CallbackData *callbackData = reinterpret_cast<CallbackData *>(data);
65       std::promise<std::string> promise;
66       auto future = promise.get_future();
67       napi_call_threadsafe_function(callbackData->tsfn, &promise, napi_tsfn_nonblocking);
68       try {
69           auto result = future.get();
70           // OH_LOG_INFO(LOG_APP, "XXX, Result from JS %{public}s", result.c_str());
71       } catch (const std::exception &e) {
72           // OH_LOG_INFO(LOG_APP, "XXX, Result from JS %{public}s", e.what());
73       }
74   }
75   ```
76   <!-- @[napi_thread_safety_cpp](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/NodeAPI/NodeAPIClassicUseCases/NodeAPIApplicationScenario/entry/src/main/cpp/thread_safety.cpp) -->
77
783. 在JS线程中执行异步回调函数。
79   ```c++
80   static constexpr int INT_NUM_2 = 2;   // int类型数值2
81   static constexpr int INT_BUF_32 = 32; // int类型字符串长度32
82
83   static napi_value ResolvedCallback(napi_env env, napi_callback_info info)
84   {
85       void *data = nullptr;
86       size_t argc = 1;
87       napi_value argv[1];
88       if (napi_get_cb_info(env, info, &argc, argv, nullptr, &data) != napi_ok) {
89           return nullptr;
90       }
91       size_t result = 0;
92       char buf[32] = {0};
93       napi_get_value_string_utf8(env, argv[0], buf, INT_BUF_32, &result);
94       reinterpret_cast<std::promise<std::string> *>(data)->set_value(std::string(buf));
95       return nullptr;
96   }
97
98   static napi_value RejectedCallback(napi_env env, napi_callback_info info)
99   {
100       void *data = nullptr;
101       if (napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &data) != napi_ok) {
102           return nullptr;
103       }
104       reinterpret_cast<std::promise<std::string> *>(data)->set_exception(
105           std::make_exception_ptr(std::runtime_error("Error in jsCallback")));
106       return nullptr;
107   }
108
109   static void CallJs(napi_env env, napi_value jsCb, void *context, void *data)
110   {
111       if (env == nullptr) {
112           return;
113       }
114       napi_value undefined = nullptr;
115       napi_value promise = nullptr;
116       napi_get_undefined(env, &undefined);
117       napi_call_function(env, undefined, jsCb, 0, nullptr, &promise);
118       napi_value thenFunc = nullptr;
119       if (napi_get_named_property(env, promise, "then", &thenFunc) != napi_ok) {
120           return;
121       }
122       napi_value resolvedCallback = nullptr;
123       napi_value rejectedCallback = nullptr;
124       napi_create_function(env, "resolvedCallback", NAPI_AUTO_LENGTH, ResolvedCallback, data,
125   					     &resolvedCallback);
126       napi_create_function(env, "rejectedCallback", NAPI_AUTO_LENGTH, RejectedCallback, data,
127   					     &rejectedCallback);
128       napi_value argv[2] = {resolvedCallback, rejectedCallback};
129       napi_call_function(env, promise, thenFunc, INT_NUM_2, argv, nullptr);
130   }
131   ```
132   <!-- @[napi_thread_safety_cpp](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/NodeAPI/NodeAPIClassicUseCases/NodeAPIApplicationScenario/entry/src/main/cpp/thread_safety.cpp) -->
133
1344. 任务执行完成后,进行资源清理回收。
135   ```c++
136   static void WorkComplete(napi_env env, napi_status status, void *data)
137   {
138       CallbackData *callbackData = reinterpret_cast<CallbackData *>(data);
139       if (callbackData == nullptr) {
140           return;
141       }
142       napi_release_threadsafe_function(callbackData->tsfn, napi_tsfn_release);
143       napi_delete_async_work(env, callbackData->work);
144       callbackData->tsfn = nullptr;
145       callbackData->work = nullptr;
146       delete callbackData;
147   }
148   ```
149   <!-- @[napi_thread_safety_cpp](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/NodeAPI/NodeAPIClassicUseCases/NodeAPIApplicationScenario/entry/src/main/cpp/thread_safety.cpp) -->
150
1515. 初始化模块并在ArkTS侧调用接口。
152   ```c++
153   // 模块初始化
154   static napi_value Init(napi_env env, napi_value exports) {
155       napi_property_descriptor desc[] = {
156           {"startThread", nullptr, StartThread, nullptr, nullptr, nullptr, napi_default, nullptr},
157       };
158       napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
159       return exports;
160   }
161   ```
162   <!-- @[napi_thread_safety_cpp](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/NodeAPI/NodeAPIClassicUseCases/NodeAPIApplicationScenario/entry/src/main/cpp/thread_safety.cpp) -->
163
164   ``` ts
165   // 接口对应的.d.ts描述
166   export const startThread: (callback: () => Promise<string>) => void;
167   ```
168   <!-- @[napi_thread_safety_dts](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/NodeAPI/NodeAPIClassicUseCases/NodeAPIApplicationScenario/entry/src/main/cpp/types/libentry1/Index.d.ts) -->
169
170   ``` ts
171   // ArkTS侧调用接口
172   import nativeModule from 'libentry.so'; // 通过import的方式,引入Native能力
173
174   let callback = (): Promise<string> => {
175     return new Promise((resolve) => {
176       setTimeout(() => {
177         resolve("string from promise");
178       }, 5000);
179     });
180   }
181   nativeModule.startThread(callback);
182   ```
183   <!-- @[napi_thread_safety_ets](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/NodeAPI/NodeAPIClassicUseCases/NodeAPIApplicationScenario/entry/src/main/ets/pages/Index.ets) -->
184