Как использовать napi_threadsafe_function для NodeJS Native Addon - PullRequest
1 голос
/ 20 ноября 2019

Я просматривал документацию 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 напрямую

Ответы [ 2 ]

1 голос
/ 02 декабря 2019

В качестве летописного тега можно сказать, что * Thread1afeFunctions N-API действует как безопасный туннель между асинхронным кодом C / C ++, выполняющимся в рабочем потоке, и уровнем JavaScript для обмена информацией .

Прежде чем перейти к технической, давайте рассмотрим сценарий, что у нас есть очень длительный процесс, тяжелая задача, которую нужно выполнить. Мы все знаем, что помещать эту задачу в основной поток node.js не очень удачный выбор, она заблокирует цикл обработки событий и заблокирует все остальные задачи в очереди. Поэтому хорошим выбором может быть рассмотрение этой задачи в отдельном потоке (назовем этот поток рабочим). JavaScript асинхронный обратный вызов и Promise делают именно этот подход.

Допустим, мы развернули задачу в рабочем потоке , и мы готовы с частью результата, и мы хотели бы, чтобы она была отправлена ​​на уровень JavaScript. Затем процесс включает в себя: преобразование результата в napi_value и затем вызов функции JavaScript Callback * из C / C ++. К сожалению ни одна из операций не может быть выполнена из рабочего потока;эти операции должны выполняться исключительно из основного потока . Обещание JavaScript и обратный вызов, дождитесь завершения задачи и затем переключитесь на основной поток вместе с задачей, что приведет к обычному хранилищу C / C ++, такому как структура и т. Д. Затем выполните преобразование napi_value и вызовите функцию обратного вызова JavaScript из основногоthread.

Поскольку наша задача выполняется очень долго, вероятно, мы не хотим ждать до конца задачи, прежде чем обмениваться результатом со слоем 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.
}

. В этих первых трех функциях почти такие же, как в Promise и Callback JavaScript, с которыми мы знакомы. Четвертый - специально для асинхронных потоковобезопасных вызовов функций. В этом случае наша долгосрочная задача выполняется функцией ExecuteWork () в рабочем потоке . Допустим, он дал нам указание не вызывать JavaScript (а также любое преобразование napi_value результата) из ExecuteWork () , но разрешено делать это из ThreadSafeCFunction4CallingJS , пока мы вызываем ThreadSafeCFunction4CallingJSс напи-эквивалентом указателя на функцию C / C ++. Затем мы можем упаковать вызовы JavaScript внутри этой функции ThreadSafeCFunction4CallingJS (). Затем, когда функция ExecuteWork () может передать результат в ThreadSafeCFunction4CallingJS (), когда он вызывается в простых единицах хранения C / C ++, таких как структура и т. Д. ThreadSafeCFunction4CallingJS () преобразует этот результат в napi_value и вызывает функцию JavaScript. Под прикрытием функция ThreadSafeCFunction4CallingJS () ставится в очередь в цикл обработки событий, и в конечном итоге она выполняется главным потоком.

Следующий фрагмент кода, упакованный внутри CAsyncStreamSearch (), отвечает за созданиеуказатель на функцию C / C ++, эквивалентный N-API, используя usng napi_create_threadsafe_function () , и это делается из основного потока нативного дополнения. Аналогично, запрос на создание рабочего потока с использованием функции napi_create_async_work () с последующим размещением работы в очереди событий на с использованием 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;
}

Затем во время асинхронного выполнения задачи (функция ExecuteWork ()) вызывает ThreadSafeCFunction4CallingJS (), вызывая функцию napi_call_threadsafe_function (), как показано ниже.

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. Когда я изучал эту концепцию, я тоже ссылался на тот же пример, во время моего исследования этот пример был воссоздан путем извлечения из него оригинальной идеи, надеюсь, вы найдете это намного упрощенным. и это доступно в

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

0 голосов
/ 20 ноября 2019

Если кто-то еще застрянет с этой проблемой. Мне наконец удалось выследить пример здесь .

Как только я пойму это лучше и получу рабочий образец, я обновлю здесь. Надеюсь, кому-то это понадобится в будущем, будет легче, чем мне.

...