1# 使用Node-API接口进行线程安全开发
2
3
4## 场景介绍
5
6napi_create_threadsafe_function是Node-API接口之一,用于创建一个线程安全的JavaScript函数。主要用于在多个线程之间共享和调用,而不会出现竞争条件或死锁。例如以下场景:
7
8
9- 异步计算:如果需要进行耗时的计算或IO操作,可以创建一个线程安全的函数,将计算或IO操作放在另一个线程中执行,避免阻塞主线程,提高程序的响应速度。
10
11- 数据共享:如果多个线程需要访问同一份数据,可以创建一个线程安全的函数,确保数据的读写操作不会发生竞争条件或死锁等问题。
12
13- 多线程编程:如果需要进行多线程编程,可以创建一个线程安全的函数,确保多个线程之间的通信和同步操作正确无误。
14
15
16## 使用示例
17
181. 在Native入口定义线程安全函数。
19   ```c++
20   #include "napi/native_api.h"
21   #include "hilog/log.h"
22   #include <future>
23
24   struct CallbackData {
25       napi_threadsafe_function tsfn;
26       napi_async_work work;
27   };
28
29   static napi_value StartThread(napi_env env, napi_callback_info info)
30   {
31       size_t argc = 1;
32       napi_value jsCb = nullptr;
33       CallbackData *callbackData = nullptr;
34       napi_get_cb_info(env, info, &argc, &jsCb, nullptr, reinterpret_cast<void **>(&callbackData));
35
36       // 创建一个线程安全函数
37       napi_value resourceName = nullptr;
38       napi_create_string_utf8(env, "Thread-safe Function Demo", NAPI_AUTO_LENGTH, &resourceName);
39       napi_create_threadsafe_function(env, jsCb, nullptr, resourceName, 0, 1, callbackData, nullptr,
40           callbackData, CallJs, &callbackData->tsfn);
41
42       // 创建一个异步任务
43       // ExecuteWork会执行在一个由libuv创建的非JS线程上,此处使用napi_create_async_work是为了模拟在非JS线程场景使用napi_call_threadsafe_function接口向JS线程提交任务
44       napi_create_async_work(env, nullptr, resourceName, ExecuteWork, WorkComplete, callbackData,
45           &callbackData->work);
46
47       // 将异步任务加入到异步队列中
48       napi_queue_async_work(env, callbackData->work);
49       return nullptr;
50   }
51   ```
52
532. 在工作线程中调用ExecuteWork,并执行线程安全函数。
54   ```c++
55   static void ExecuteWork(napi_env env, void *data)
56   {
57       CallbackData *callbackData = reinterpret_cast<CallbackData *>(data);
58       std::promise<std::string> promise;
59       auto future = promise.get_future();
60       napi_call_threadsafe_function(callbackData->tsfn, &promise, napi_tsfn_nonblocking);
61       try {
62           auto result = future.get();
63           // OH_LOG_INFO(LOG_APP, "XXX, Result from JS %{public}s", result.c_str());
64       } catch (const std::exception &e) {
65           // OH_LOG_INFO(LOG_APP, "XXX, Result from JS %{public}s", e.what());
66       }
67   }
68   ```
69
703. 在JS线程执行异步回调函数。
71   ```c++
72   static napi_value ResolvedCallback(napi_env env, napi_callback_info info)
73   {
74       void *data = nullptr;
75       size_t argc = 1;
76       napi_value argv[1];
77       if (napi_get_cb_info(env, info, &argc, argv, nullptr, &data) != napi_ok) {
78           return nullptr;
79       }
80       size_t result = 0;
81       char buf[32] = {0};
82       napi_get_value_string_utf8(env, argv[0], buf, 32, &result);
83       reinterpret_cast<std::promise<std::string> *>(data)->set_value(std::string(buf));
84       return nullptr;
85   }
86
87   static napi_value RejectedCallback(napi_env env, napi_callback_info info)
88   {
89       void *data = nullptr;
90       if (napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &data) != napi_ok) {
91           return nullptr;
92       }
93       reinterpret_cast<std::promise<std::string> *>(data)->set_exception(
94           std::make_exception_ptr(std::runtime_error("Error in jsCallback")));
95       return nullptr;
96   }
97
98   static void CallJs(napi_env env, napi_value jsCb, void *context, void *data)
99   {
100       if (env == nullptr) {
101           return;
102       }
103       napi_value undefined = nullptr;
104       napi_value promise = nullptr;
105       napi_get_undefined(env, &undefined);
106       napi_call_function(env, undefined, jsCb, 0, nullptr, &promise);
107       napi_value thenFunc = nullptr;
108       if (napi_get_named_property(env, promise, "then", &thenFunc) != napi_ok) {
109           return;
110       }
111       napi_value resolvedCallback;
112       napi_value rejectedCallback;
113       napi_create_function(env, "resolvedCallback", NAPI_AUTO_LENGTH, ResolvedCallback, data,
114   					     &resolvedCallback);
115       napi_create_function(env, "rejectedCallback", NAPI_AUTO_LENGTH, RejectedCallback, data,
116   					     &rejectedCallback);
117       napi_value argv[2] = {resolvedCallback, rejectedCallback};
118       napi_call_function(env, promise, thenFunc, 2, argv, nullptr);
119   }
120   ```
121
1224. 任务执行完成后,进行资源清理回收。
123   ```c++
124   static void WorkComplete(napi_env env, napi_status status, void *data)
125   {
126       CallbackData *callbackData = reinterpret_cast<CallbackData *>(data);
127       napi_release_threadsafe_function(callbackData->tsfn, napi_tsfn_release);
128       napi_delete_async_work(env, callbackData->work);
129       callbackData->tsfn = nullptr;
130       callbackData->work = nullptr;
131   }
132   ```
133
1345. 模块初始化以及ArkTS侧调用接口。
135   ```c++
136   // 模块初始化
137   static napi_value Init(napi_env env, napi_value exports) {
138       CallbackData *callbackData = new CallbackData(); // 可在线程退出时释放
139       napi_property_descriptor desc[] = {
140           {"startThread", nullptr, StartThread, nullptr, nullptr, nullptr, napi_default, callbackData},
141       };
142       napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
143       return exports;
144   }
145   ```
146
147   ``` ts
148   // 接口对应的.d.ts描述
149    export const startThread: (callback: () => Promise<string>) => void;
150
151   // ArkTS侧调用接口
152   import nativeModule from 'libentry.so'; // 通过import的方式,引入Native能力
153
154   let callback = (): Promise<string> => {
155     return new Promise((resolve) => {
156       setTimeout(() => {
157           resolve("string from promise");
158         }, 5000);
159       });
160    }
161    nativeModule.startThread(callback);
162   ```
163