C 如何为NodeJS本机插件使用napi_threadsafe_函数

C 如何为NodeJS本机插件使用napi_threadsafe_函数,c,asynchronous,node.js-addon,n-api,C,Asynchronous,Node.js Addon,N Api,我一直在浏览,试图了解它是如何处理多线程的。根据文档napi\u create\u threadsafe\u function()和napi\u call\u threadsafe\u function()用于从多个线程创建和调用js函数。问题是文档并不是那么直截了当,没有任何示例,我在其他任何地方都找不到 如果任何人有使用napi\u create\u threadsafe\u function()和napi\u call\u threadsafe\u function()的经验,或者知道在哪

我一直在浏览,试图了解它是如何处理
多线程的。根据文档
napi\u create\u threadsafe\u function()
napi\u call\u threadsafe\u function()
用于从多个线程创建和调用
js函数。问题是文档并不是那么直截了当,没有任何示例,我在其他任何地方都找不到

如果任何人有使用
napi\u create\u threadsafe\u function()
napi\u call\u threadsafe\u function()
的经验,或者知道在哪里可以找到使用它们的示例。请你帮我举一个基本的例子,这样我就能理解如何正确使用它们


我正在写一个
C
插件而不是
C++
,需要使用这些函数。我不是在使用包装器
节点插件api
,而是直接使用
napi
,如果有人遇到这个问题。我终于找到了一个例子

一旦我更好地理解了它并获得了一个工作样本,我将在这里更新。希望有人在未来需要这将有一个比我容易的时间


参见Satyan的答案,我们可以说,N-API ThreadSafe函数充当在工作线程上执行的异步C/C++代码和用于信息交换的JavaScript层之间的安全通道

<>技术之前,让我们考虑一个场景,我们有一个很长的运行过程繁重的任务要完成。我们都知道,将此任务放在node.js主线程上不是一个好选择,它会阻塞事件循环并阻塞队列中的所有其他任务。因此,一个好的选择是在一个单独的线程中考虑这个任务(我们把这个线程称为一个工作者线程)。JavaScript异步回调和Promise正是这样做的

假设我们已经在一个工作线程上部署了任务,我们已经准备好了一部分结果,我们希望将其发送到JavaScript层。然后,该过程包括,将结果转换为napi_值,然后从C/C++调用回调JavaScript函数。不幸的是,这两个操作都不能从工作线程执行;这些操作只能从主线程执行。JavaScript承诺和回调,等待任务完成,然后在正常的C/C++存储设施(如结构等)中随任务结果切换到主线程。然后进行napi_值转换,并从主线程调用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.
}
在本文中,前三个函数与我们熟悉的JavaScript承诺和回调几乎相同。第四个函数专门用于异步线程安全函数调用。在这里,我们的长时间运行的任务是由工作线程上的ExecuteWork()函数执行的。让我们说,它指示我们不要从ExecuteWork()调用JavaScript(以及结果的任何napi值转换),但允许从ThreadSafeCFunction4CallingJS调用JavaScript,只要我们使用相当于C/C++函数指针的napi调用ThreadSafeCFunction4CallingJS即可。然后我们可以将JavaScript调用打包到这个ThreadSafeCFunction4CallingJS()函数中。然后,当在普通C/C++存储单元(如结构等)中调用时,ExecuteWork()函数可以将结果传递给ThreadSafeCFunction4CallingJS()。ThreadSafeCFunction4CallingJS()将此结果转换为napi_值并调用JavaScript函数。 在封面下,ThreadSafeCFunction4CallingJS()函数正在排队等待事件循环,最终由主线程执行。

CAsyncStreamSearch()中打包的以下代码片段负责通过usngnapi_create_threadsafe_function()创建一个与N-API等效的C/C++函数指针,它是从本机插件的主线程本身完成的。类似地,通过使用napi\u create\u async\u work()函数创建工作线程的请求,然后通过使用napi\u queue\u async\u 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;
}
然后在异步执行任务的过程中(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,);
}
您指出的示例是最好的信息源之一,它直接来自node.js团队本身。当我学习这个概念的时候,我也引用了同样的例子,在我的学习过程中,这个例子是通过从中提取原始想法而重新创建的,希望你能发现这个简化了很多。并于


这个网站的解决方案对我有效

struct-ThreadCtx{
ThreadCtx(Napi::Env Env){};
std::thread nativeThread;
Napi::线程安全函数tsfn;
};
void Target::Connect(const Napi::CallbackInfo&info){
Napi::Env Env=info.Env();
螺纹CTX=新螺纹CTX(环境);
//创建一个线程安全函数
threadCtx->tsfn=Napi::ThreadSafeFunction::New
struct ThreadCtx {
  ThreadCtx(Napi::Env env) {};

  std::thread nativeThread;
  Napi::ThreadSafeFunction tsfn;
};


void Target::Connect(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();


  threadCtx = new ThreadCtx(env);

  // Create a ThreadSafeFunction
  threadCtx->tsfn = Napi::ThreadSafeFunction::New(env, info[0].As<Napi::Function>(), "Resource Name", 0 /* Unlimited queue */, 1 /* Only 1 thread */, threadCtx,
        [&]( Napi::Env, void *finalizeData, ThreadCtx *context ) {
            printf("Thread cleanup\n");
            threadCtx->nativeThread.join();
        },
        (void*)nullptr
        );

  // Create a native thread
  threadCtx->nativeThread = std::thread([&] {
    auto callback = [](Napi::Env env, Napi::Function cb, char* buffer) {
        cb.Call({Napi::String::New(env, buffer)});
    };



    char reply[1024];
    memset(reply, 0, sizeof(reply));
    while(true)
    {
        size_t reply_length = boost::asio::read(s, boost::asio::buffer(reply, sizeof(reply)));

        if(reply_length <= 0) {
            printf("Bad read from boost asio\n");
            break;
        }


        // Callback (blocking) to JS
        napi_status status = threadCtx->tsfn.BlockingCall(reply, callback);
        if (status != napi_ok)
        {
            // Handle error
            break;
        }
    }

    // Release the thread-safe function
    threadCtx->tsfn.Release();
  });
}