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