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 constexpr int INT_NUM_2 = 2; // int类型数值2 73 static constexpr int INT_BUF_32 = 32; // int类型字符串长度32 74 75 static napi_value ResolvedCallback(napi_env env, napi_callback_info info) 76 { 77 void *data = nullptr; 78 size_t argc = 1; 79 napi_value argv[1]; 80 if (napi_get_cb_info(env, info, &argc, argv, nullptr, &data) != napi_ok) { 81 return nullptr; 82 } 83 size_t result = 0; 84 char buf[32] = {0}; 85 napi_get_value_string_utf8(env, argv[0], buf, INT_BUF_32, &result); 86 reinterpret_cast<std::promise<std::string> *>(data)->set_value(std::string(buf)); 87 return nullptr; 88 } 89 90 static napi_value RejectedCallback(napi_env env, napi_callback_info info) 91 { 92 void *data = nullptr; 93 if (napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &data) != napi_ok) { 94 return nullptr; 95 } 96 reinterpret_cast<std::promise<std::string> *>(data)->set_exception( 97 std::make_exception_ptr(std::runtime_error("Error in jsCallback"))); 98 return nullptr; 99 } 100 101 static void CallJs(napi_env env, napi_value jsCb, void *context, void *data) 102 { 103 if (env == nullptr) { 104 return; 105 } 106 napi_value undefined = nullptr; 107 napi_value promise = nullptr; 108 napi_get_undefined(env, &undefined); 109 napi_call_function(env, undefined, jsCb, 0, nullptr, &promise); 110 napi_value thenFunc = nullptr; 111 if (napi_get_named_property(env, promise, "then", &thenFunc) != napi_ok) { 112 return; 113 } 114 napi_value resolvedCallback; 115 napi_value rejectedCallback; 116 napi_create_function(env, "resolvedCallback", NAPI_AUTO_LENGTH, ResolvedCallback, data, 117 &resolvedCallback); 118 napi_create_function(env, "rejectedCallback", NAPI_AUTO_LENGTH, RejectedCallback, data, 119 &rejectedCallback); 120 napi_value argv[2] = {resolvedCallback, rejectedCallback}; 121 napi_call_function(env, promise, thenFunc, INT_NUM_2, argv, nullptr); 122 } 123 ``` 124 1254. 任务执行完成后,进行资源清理回收。 126 ```c++ 127 static void WorkComplete(napi_env env, napi_status status, void *data) 128 { 129 CallbackData *callbackData = reinterpret_cast<CallbackData *>(data); 130 napi_release_threadsafe_function(callbackData->tsfn, napi_tsfn_release); 131 napi_delete_async_work(env, callbackData->work); 132 callbackData->tsfn = nullptr; 133 callbackData->work = nullptr; 134 } 135 ``` 136 1375. 模块初始化以及ArkTS侧调用接口。 138 ```c++ 139 // 模块初始化 140 static napi_value Init(napi_env env, napi_value exports) { 141 CallbackData *callbackData = new CallbackData(); // 可在线程退出时释放 142 napi_property_descriptor desc[] = { 143 {"startThread", nullptr, StartThread, nullptr, nullptr, nullptr, napi_default, callbackData}, 144 }; 145 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 146 return exports; 147 } 148 ``` 149 150 ``` ts 151 // 接口对应的.d.ts描述 152 export const startThread: (callback: () => Promise<string>) => void; 153 154 // ArkTS侧调用接口 155 import nativeModule from 'libentry.so'; // 通过import的方式,引入Native能力 156 157 let callback = (): Promise<string> => { 158 return new Promise((resolve) => { 159 setTimeout(() => { 160 resolve("string from promise"); 161 }, 5000); 162 }); 163 } 164 nativeModule.startThread(callback); 165 ``` 166