Node.js v20.3.1 文档


异步线程安全函数调用#

JavaScript 函数通常只能从原生插件的主线程调用。 如果插件创建了额外的线程,则不得从这些线程调用需要 napi_envnapi_valuenapi_ref 的 Node-API 函数。

当插件有额外的线程并且需要根据这些线程完成的处理调用 JavaScript 函数时,这些线程必须与插件的主线程通信,以便主线程可以代表它们调用 JavaScript 函数。 线程安全函数 API 提供了一种简单的方法来执行此操作。

这些 API 提供了类型 napi_threadsafe_function 以及创建、销毁和调用该类型对象的 API。 napi_create_threadsafe_function() 创建对 napi_value 的持久引用,该引用包含可从多个线程调用的 JavaScript 函数。 调用异步发生。 这意味着调用 JavaScript 回调的值将被放置在一个队列中,并且对于队列中的每个值,最终将调用 JavaScript 函数。

创建 napi_threadsafe_function 后,可以提供 napi_finalize 回调。 当线程安全函数即将被销毁时,将在主线程上调用此回调。 它接收构造期间给出的上下文和最终数据,并提供在线程之后进行清理的机会,例如 通过调用 uv_thread_join()除了主循环线程之外,在 finalize 回调完成后,没有线程应该使用线程安全函数。

在调用 napi_create_threadsafe_function() 期间给出的 context 可以从调用 napi_get_threadsafe_function_context() 的任何线程中检索。

调用线程安全的函数#

napi_call_threadsafe_function() 可用于启动对 JavaScript 的调用。 napi_call_threadsafe_function() 接受一个参数,该参数控制 API 是否以阻塞方式运行。 如果设置为 napi_tsfn_nonblocking,API 将以非阻塞方式运行,如果队列已满则返回 napi_queue_full,从而阻止数据成功添加到队列中。 如果设置为 napi_tsfn_blocking,API 将阻塞,直到队列中有可用空间。 如果创建的线程安全函数的最大队列大小为 0,则 napi_call_threadsafe_function() 永远不会阻塞。

不应从 JavaScript 线程使用 napi_tsfn_blocking 调用 napi_call_threadsafe_function(),因为如果队列已满,可能会导致 JavaScript 线程死锁。

对 JavaScript 的实际调用由通过 call_js_cb 参数给出的回调控制。 对于通过成功调用 napi_call_threadsafe_function() 放入队列中的每个值,在主线程上调用 call_js_cb 一次。 如果没有给出这样的回调,将使用默认回调,并且生成的 JavaScript 调用将没有参数。 call_js_cb 回调在其参数中接收要作为 napi_value 调用的 JavaScript 函数,以及创建 napi_threadsafe_function 时使用的 void* 上下文指针,以及由其中一个辅助线程创建的下一个数据指针。 然后,回调可以使用诸如 napi_call_function() 之类的 API 来调用 JavaScript。

回调也可以在 envcall_js_cb 都设置为 NULL 的情况下调用,以指示不再可能调用 JavaScript,而项目仍保留在可能需要释放的队列中。 这通常发生在 Node.js 进程退出而线程安全功能仍处于活动状态时。

没有必要通过 napi_make_callback() 调用 JavaScript,因为 Node-API 在适合回调的上下文中运行 call_js_cb

线程安全函数的引用计数#

napi_threadsafe_function 对象存在期间,可以将线程添加到 napi_threadsafe_function 对象或从中删除。 因此,除了在创建时指定线程的初始数量外,还可以调用 napi_acquire_threadsafe_function 来指示新线程将开始使用线程安全函数。 同样,可以调用 napi_release_threadsafe_function 来指示现有线程将停止使用线程安全函数。

当使用该对象的每个线程调用 napi_release_threadsafe_function() 或收到 napi_closing 的返回状态以响应对 napi_call_threadsafe_function 的调用时,napi_threadsafe_function 对象将被销毁。 在 napi_threadsafe_function 被销毁之前队列被清空。 napi_release_threadsafe_function() 应该是与给定的 napi_threadsafe_function 一起进行的最后一次 API 调用,因为在调用完成后,无法保证 napi_threadsafe_function 仍然被分配。 出于同样的原因,在收到 napi_closing 的返回值以响应对 napi_call_threadsafe_function 的调用后,不要使用线程安全函数。 可以在传递给 napi_create_threadsafe_function()napi_finalize 回调中释放与 napi_threadsafe_function 关联的数据。 napi_create_threadsafe_function 的参数 initial_thread_count 表示线程安全函数的初始获取次数,而不是在创建时多次调用 napi_acquire_threadsafe_function

一旦使用 napi_threadsafe_function 的线程数达到零,则没有其他线程可以通过调用 napi_acquire_threadsafe_function() 开始使用它。 事实上,除 napi_release_threadsafe_function() 之外的所有后续 API 调用都将返回错误值 napi_closing

线程安全函数可以通过给 napi_release_threadsafe_function() 赋值 napi_tsfn_abort 来实现 "aborted"。 这将导致除 napi_release_threadsafe_function() 之外与线程安全函数关联的所有后续 API 甚至在其引用计数达到零之前就返回 napi_closing。 特别是,napi_call_threadsafe_function() 将返回 napi_closing,从而通知线程不再可能对线程安全函数进行异步调用。 这可以用作终止线程的标准。 napicallthreadsafefunction() 接收到 napiclosing 的返回值后,线程不得再使用线程安全函数,因为不再保证会分配它。

决定是否保持进程运行#

与 libuv 句柄类似,线程安全函数可以是 "referenced" 和 "unreferenced"。 "referenced" 线程安全函数将导致创建它的线程上的事件循环保持活动状态,直到线程安全函数被销毁。 相反,"unreferenced" 线程安全函数不会阻止事件循环退出。 API napi_ref_threadsafe_functionnapi_unref_threadsafe_function 就是为此目的而存在的。

napi_unref_threadsafe_function 既没有将线程安全函数标记为可以被销毁,也没有 napi_ref_threadsafe_function 阻止它被销毁。

napi_create_threadsafe_function#

NAPI_EXTERN napi_status
napi_create_threadsafe_function(napi_env env,
                                napi_value func,
                                napi_value async_resource,
                                napi_value async_resource_name,
                                size_t max_queue_size,
                                size_t initial_thread_count,
                                void* thread_finalize_data,
                                napi_finalize thread_finalize_cb,
                                void* context,
                                napi_threadsafe_function_call_js call_js_cb,
                                napi_threadsafe_function* result); 
  • [in] env: 调用 API 的环境。
  • [in] func: 从另一个线程调用的可选 JavaScript 函数。 如果 NULL 传递给 call_js_cb,则必须提供它。
  • [in] async_resource: 与将传递给可能的 async_hooks init 钩子 的异步工作关联的可选对象。
  • [in] async_resource_name: 一个 JavaScript 字符串,用于为 async_hooks API 公开的诊断信息提供的资源类型提供标识符。
  • [in] max_queue_size: 队列的最大大小。 0 为无限制。
  • [in] initial_thread_count: 初始获取数,即初始线程数,包括将使用此函数的主线程。
  • [in] thread_finalize_data: 要传递给 thread_finalize_cb 的可选数据。
  • [in] thread_finalize_cb: napi_threadsafe_function 被销毁时调用的可选函数。
  • [in] context: 附加到生成的 napi_threadsafe_function 的可选数据。
  • [in] call_js_cb: 可选回调调用 JavaScript 函数以响应不同线程上的调用。 此回调将在主线程上调用。 如果没有给出,JavaScript 函数将在没有参数的情况下调用,并将 undefined 作为其 this 值。 napi_threadsafe_function_call_js 提供了更多详细信息。
  • [out] result: 异步线程安全的 JavaScript 函数。

napi_get_threadsafe_function_context#

NAPI_EXTERN napi_status
napi_get_threadsafe_function_context(napi_threadsafe_function func,
                                     void** result); 
  • [in] func: 为其检索上下文的线程安全函数。
  • [out] result: 存储上下文的位置。

可以从使用 func 的任何线程调用此 API。

napi_call_threadsafe_function#

NAPI_EXTERN napi_status
napi_call_threadsafe_function(napi_threadsafe_function func,
                              void* data,
                              napi_threadsafe_function_call_mode is_blocking); 
  • [in] func: 要调用的异步线程安全 JavaScript 函数。
  • [in] data: 通过创建线程安全 JavaScript 函数期间提供的回调 call_js_cb 发送到 JavaScript 的数据。
  • [in] is_blocking: 标志,其值可以是 napi_tsfn_blocking,表示如果队列已满,调用应该阻塞;也可以是 napi_tsfn_nonblocking,表示只要队列已满,调用就应该立即返回,状态为 napi_queue_full

不应从 JavaScript 线程使用 napi_tsfn_blocking 调用此 API,因为如果队列已满,可能会导致 JavaScript 线程死锁。

如果从任何线程调用 napi_release_threadsafe_function() 并将 abort 设置为 napi_tsfn_abort,则此 API 将返回 napi_closing。 只有当 API 返回 napi_ok 时,该值才会添加到队列中。

可以从使用 func 的任何线程调用此 API。

napi_acquire_threadsafe_function#

NAPI_EXTERN napi_status
napi_acquire_threadsafe_function(napi_threadsafe_function func); 
  • [in] func: 开始使用异步线程安全的 JavaScript 函数。

线程应在将 func 传递给任何其他线程安全函数 API 以指示它将使用 func 之前调用此 API。 这可以防止 func 在所有其他线程停止使用它时被销毁。

可以从将开始使用 func 的任何线程调用此 API。

napi_release_threadsafe_function#

NAPI_EXTERN napi_status
napi_release_threadsafe_function(napi_threadsafe_function func,
                                 napi_threadsafe_function_release_mode mode); 
  • [in] func: 异步线程安全的 JavaScript 函数,其引用计数将减少。
  • [in] mode: 其值可以是 napi_tsfn_release 的标志,表示当前线程将不再调用线程安全函数,或者是 napi_tsfn_abort,表示除了当前线程之外,没有其他线程应该进一步调用线程安全函数 功能。 如果设置为 napi_tsfn_abort,对 napi_call_threadsafe_function() 的进一步调用将返回 napi_closing,并且不会将更多值放入队列中。

线程停止使用 func 时应调用此 API。 在调用此 API 后将 func 传递给任何线程安全的 API 会产生未定义的结果,因为 func 可能已被销毁。

可以从任何将停止使用 func 的线程调用此 API。

napi_ref_threadsafe_function#

NAPI_EXTERN napi_status
napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func); 
  • [in] env: 调用 API 的环境。
  • [in] func: 要引用的线程安全函数。

此 API 用于指示在主线程上运行的事件循环在 func 被销毁之前不应退出。 与 uv_ref 类似,它也是幂等的。

napi_unref_threadsafe_function 既没有将线程安全函数标记为可以被销毁,也没有 napi_ref_threadsafe_function 阻止它被销毁。 napi_acquire_threadsafe_functionnapi_release_threadsafe_function 可用于该目的。

此 API 只能从主线程调用。

napi_unref_threadsafe_function#

NAPI_EXTERN napi_status
napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func); 
  • [in] env: 调用 API 的环境。
  • [in] func: 要取消引用的线程安全函数。

该 API 用于指示在主线程上运行的事件循环可能会在 func 被销毁之前退出。 与 uv_unref 类似,它也是幂等的。

此 API 只能从主线程调用。