如何将 napi_threadsafe_function 用于 NodeJS Native Addon

Fre*_*sen 4 c asynchronous node.js-addon n-api

我一直在浏览NAPI 文档,试图了解它是如何处理multithreading. 根据文档napi_create_threadsafe_function()napi_call_threadsafe_function()用于js functions从多个线程创建和调用。问题是文档不是那么直截了当,并且没有示例,我在其他任何地方都找不到。

如果有人有任何使用经验napi_create_threadsafe_function()和/napi_call_threadsafe_function()或知道在哪里可以找到使用它们的示例。请如果你能帮助一个基本的例子,这样我就可以理解如何正确使用它们。

我正在C编写一个插件,而不是C++需要使用这些功能。我没有使用包装器node-addon-api,而是napi直接使用

Sat*_*yan 7

作为一个总结标签,我们可以说,N-API ThreadSafeFunctions 充当在工作线程上执行的异步 C/C++ 代码和用于信息交换的 JavaScript 层之间的安全隧道

在进入技术之前,让我们考虑一个场景,我们有一个非常长的运行过程需要完成的繁重任务。我们都知道把这个任务放在 node.js 主线程上不是一个好的选择,它会阻塞事件循环并阻塞队列中的所有其他任务。所以一个好的选择可能是在一个单独的线程中考虑这个任务(让我们把这个线程称为工作线程)。JavaScript 异步回调和 Promise 正是采用这种方法。

假设我们已经在工作线程上部署了任务,我们已经准备好了一部​​分结果,我们希望它被发送到 JavaScript 层。然后涉及的过程是,将结果转换为 napi_value,然后从 C/C++ 中调用 Callback JavaScript函数。不幸的是,这两个操作都不能从工作线程执行;这些操作应该完全在主线程中完成。JavaScript Promise 和 Callback,等待任务完成,然后切换到主线程,任务结果在一个普通的 C/C++ 存储设施如结构等中,然后进行 napi_value 转换并从 main 调用 JavaScript 回调函数线。

由于我们的任务运行时间非常长,我们可能不想等到任务结束再与 JavaScript 层交换结果。让我们考虑一个场景,我们在一个非常大的视频中搜索对象,我们更喜欢在找到对象时将检测到的对象发送到 JavaScript 层。在这种情况下,我们将不得不在任务仍在进行中时开始发送任务结果这是异步线程安全函数调用来寻求我们帮助的场景。它充当工作线程和 JavaScript 层之间用于信息交换的安全隧道。让我们考虑以下函数片段

napi_value CAsyncStreamSearch(napi_env env, napi_callback_info info)
{
    // The native addon function exposed to JavaScript
    // This will be the funciton a node.js application calling.
}

void ExecuteWork(napi_env env, void* data)
{
    // We will use this function to get the task done.
    // This code will be executed on a worker thread.
}

void OnWorkComplete(napi_env env, napi_status status, void* data)
{
    // after the `ExecuteWork` function exits, this
    // callback function will be called on the main thread
}

void ThreadSafeCFunction4CallingJS(napi_env env, napi_value js_cb,
                 void* context, void* data)
{
   // This funcion acts as a safe tunnel between the asynchronous C/C++ code 
   // executing the worker thread and the JavaScript layer for information exchange.
}
Run Code Online (Sandbox Code Playgroud)

前三个函数与我们熟悉的 JavaScript Promise 和 Callback 几乎相同。第四个是专门针对异步线程安全函数调用的。在这里,我们长时间运行的任务由工作线程上的 ExecuteWork() 函数执行。假设它指示我们不要从 ExecuteWork()调用 JavaScript(以及任何结果的 napi_value 转换),但允许从 ThreadSafeCFunction4CallingJS调用只要我们使用与 C/C++ 函数指针等效的 napi 调用 ThreadSafeCFunction4CallingJS。然后我们可以将 JavaScript 调用打包到这个 ThreadSafeCFunction4CallingJS() 函数中。然后当 ExecuteWork() 函数可以将结果传递给 ThreadSafeCFunction4CallingJS() 时,它正在一个普通的 C/C++ 存储单元(如结构等)中被调用。 ThreadSafeCFunction4CallingJS() 将此结果转换为 napi_value 并调用 JavaScript 函数。 在幕后,ThreadSafeCFunction4CallingJS() 函数正在排队等待事件循环,并最终由主线程执行。

下面的代码段填充内部CAsyncStreamSearch()是负责通过USNG创建C / C ++函数指针当量N- API的napi_create_threadsafe_function() ,它被从本地插件的主线程本身完成。类似地,通过使用用于创建工作线程的请求napi_create_async_work()函数然后将INT事件队列中的工作通过 使用napi_queue_async_work()在未来,使得工作者线程将拾取这个项目。

napi_value CAsyncStreamSearch(napi_env env, napi_callback_info info)
{
-- -- -- --
-- -- -- --
  // Create a thread-safe N-API callback function correspond to the C/C++ callback function
  napi_create_threadsafe_function(env,
      js_cb, NULL, work_name, 0, 1, NULL, NULL, NULL,
      ThreadSafeCFunction4CallingJS, // the C/C++ callback function
      // out: the asynchronous thread-safe JavaScript function
      &(async_stream_data_ex->tsfn_StreamSearch));

  // Create an async work item, that can be deployed in the node.js event queue
  napi_create_async_work( env, NULL,
       work_name,
       ExecuteWork,
       OnWorkComplete,
       async_stream_data_ex,
       // OUT: THE handle to the async work item
       &(async_stream_data_ex->work_StreamSearch);)

  // Queue the work item for execution.
  napi_queue_async_work(env, async_stream_data_ex->work_StreamSearch);

  return NULL;
}
Run Code Online (Sandbox Code Playgroud)

然后在异步执行任务期间(ExecuteWork() 函数)通过​​调用 napi_call_threadsafe_function() 函数调用 ThreadSafeCFunction4CallingJS(),如下所示。

static void ExecuteWork(napi_env env, void *data)
{
  // tsfn is napi equivalent of point to ThreadSafeCFunction4CallingJS
  // function that we created at CAsyncStreamSearch function
  napi_acquire_threadsafe_function( tsfn )
  Loop
  {
    // this will eventually invoke ThreadSafeCFunction4CallingJS()
   // we may call any number of time (in fact it can be called from any thread)
    napi_call_threadsafe_function( tsfn, WorkResult, );
  }
  napi_release_threadsafe_function( tsfn,);
}
Run Code Online (Sandbox Code Playgroud)

你指出的例子是最好的信息来源之一,它直接来自 node.js 团队本身。当我学习这个概念时,我也提到了同一个例子,在我的学习过程中,这个例子是通过从中提取原始想法重新创建的,希望你会发现这已经简化了。它可以在

https://github.com/msatyan/MyNodeC/blob/master/src/mync1/ThreadSafeAsyncStream.cpp https://github.com/msatyan/MyNodeC/blob/master/test/ThreadSafeAsyncStream.js