文档管理中心

NAPI使用线程安全函数相关问题

问题现象

调用napi_create_threadsafe_function创建ArkTS回调接口和线程安全回调函数,传入线程安全回调函数的ArkTS侧回调接口函数地址为0,无法正确获取到ArkTS侧回调函数的原始值。线程安全回调函数代码如下:

运行结果如下:

背景知识

Node-API线程安全开发主要用于异步多线程之间共享和调用场景中使用,以避免出现竞争条件或死锁。例如以下场景:

  • 异步计算:如果需要进行耗时的计算或IO操作,可以创建一个线程安全函数,将计算或IO操作放在另一个线程中执行,避免阻塞主线程,提高程序的响应速度。
  • 数据共享:如果多个线程需要访问同一份数据,可以创建一个线程安全函数,确保数据的读写操作不会发生竞争条件或死锁等问题。
  • 多线程开发:如果需要进行多线程开发,可以创建一个线程安全函数,确保多个线程之间的通信和同步操作正确无误。

napi_create_threadsafe_function:该接口主要用于创建线程安全对象,在创建的过程中,会注册异步过程中的关键信息:ArkTS回调接口callback和线程安全回调函数call_js_cb等。参数说明如下:

收起
自动换行
深色代码主题
复制
  1. /**
  2. * @brief 用于创建一个线程安全的函数,该函数可以在多个线程中调用,而不需要担心数据竞争或其他线程安全问题
  3. *
  4. * @param env 指向NAPI环境的指针,用于创建和操作Javascript值
  5. * @param func 指向JavaScript函数的指针
  6. * @param async_resource 异步资源,通常是一个表示异步操作的对象
  7. * @param async_resource_name 指向资源名称的指针,这个名称将用于日志和调试
  8. * @param max_queue_size 一个整数,表示队列的最大大小,当队列满时,新的调用将被丢弃
  9. * @param initial_thread_count 无符号整数,表示在创建线程安全函数时,初始的线程数量
  10. * @param thread_finalize_data 一个指向在所有线程之前需要清理的数据
  11. * @param napi_finalize thread_finalize_cb 回调函数,当所有线程完成时被调用,用于清理资源
  12. * @param context 指向上下文的指针,这个上下文将被传递给call_js_func函数
  13. * @param call_js_cb 指向回调函数的指针,这个函数将在Javascript函数被调用时被调用
  14. * @param result 指向napi_threadsafe_function结构的指针,这个结构将被填充为新创建的线程安全函数
  15. */
  16. napi_status napi_create_threadsafe_function(napi_env env,
  17. napi_value func,
  18. napi_value async_resource,
  19. napi_value async_resource_name,
  20. size_t max_queue_size,
  21. size_t initial_thread_count,
  22. void* thread_finalize_data,
  23. napi_finalize thread_finalize_cb,
  24. void* context,
  25. napi_threadsafe_function_call_js call_js_cb,
  26. napi_threadsafe_function* result);

问题定位

根据代码分析,发现ArkTS侧回调接口传入到线程安全函数的第三个参数,此参数关联async_hooks,用来追踪异步资源的API。

分析结论

根据napi_create_threadsafe_function线程安全函数参数说明,ArkTS应用侧传入的回调接口应该传入到第二个参数。

修改建议

ArkTS应用侧传入的回调接口参数传入到napi_create_threadsafe_function函数的第二个参数。通过线程安全回调函数CallJs执行,通过napi_call_function调用ArkTS回调接口,从而将异步计算结果反馈到ArkTS应用侧,用于应用侧UI刷新。示例代码如下:

收起
自动换行
深色代码主题
复制
  1. static napi_value StartThread(napi_env env, napi_callback_info info)
  2. {
  3. size_t argc = 1;
  4. napi_value jsCb = nullptr;
  5. CallbackData *callbackData = new CallbackData(); // 异步任务完成时释放
  6. napi_get_cb_info(env, info, &argc, &jsCb, nullptr, nullptr);
  7. // 创建一个线程安全函数
  8. napi_value resourceName = nullptr;
  9. napi_create_string_utf8(env, "Thread-safe Function Demo", NAPI_AUTO_LENGTH, &resourceName);
  10. napi_create_threadsafe_function(env, jsCb, nullptr, resourceName, 0, 1, nullptr, nullptr, nullptr, CallJs,
  11. &callbackData->tsfn);
  12. // 创建一个异步任务
  13. // ExecuteWork会执行在一个由libuv创建的非JS线程上,此处使用napi_create_async_work是为了模拟在非JS线程场景使用napi_call_threadsafe_function接口向JS线程提交任务
  14. napi_create_async_work(env, nullptr, resourceName, ExecuteWork, WorkComplete, callbackData, &callbackData->work);
  15. // 将异步任务加入到异步队列中
  16. napi_queue_async_work(env, callbackData->work);
  17. return nullptr;
  18. }
在 FAQ 中进行搜索
请输入您想要搜索的关键词