На этой странице

JavaScript-функции обычно можно вызывать только из главного потока нативного аддона. Если аддон создаёт дополнительные потоки, то функции node-addon-api, которым требуется Napi::Env, Napi::Value или Napi::Reference, не должны вызываться из этих потоков.

Когда у аддона есть дополнительные потоки и JavaScript-функции нужно вызывать на основе обработки, завершённой этими потоками, эти потоки должны общаться с главным потоком аддона, чтобы главный поток вызвал JavaScript-функцию от их имени. API thread-safe функций предоставляют простой способ это сделать.

Thread-safe функция создаётся в главном потоке через ThreadSafeFunction::New:

New(napi_env env,
    const Function& callback,
    const Object& resource,
    ResourceString resourceName,
    size_t maxQueueSize,
    size_t initialThreadCount,
    ContextType* context,
    Finalizer finalizeCallback,
    FinalizerDataType* data);

Thread-safe функция инкапсулирует:

  • Очередь сообщений: запросы на запуск JavaScript-функции помещаются в очередь, обрабатываемую асинхронно главным потоком. Количество записей, допустимых в очереди, прежде чем NonBlockingCall() вернёт ошибку «queue full», управляется параметром maxQueueSize (укажите 0 для неограниченной очереди)
  • JavaScript-функцию: колбэк для запуска (параметр callback). Эта функция либо (a) автоматически запускается без аргументов при вызове через перегрузки [Non]BlockingCall() без аргументов, либо (b) передаётся аргументом в функцию-колбэк, предоставленную в перегрузках [Non]BlockingCall(DataType* data, Callback callback).
  • Контекст: опциональные произвольные данные (параметр context) для связывания с thread-safe функцией.
  • Финализатор: опциональный колбэк (параметр finalizeCallback) для запуска при уничтожении thread-safe функции, когда все потоки закончили её использовать.
  • Данные финализатора: опциональные данные (параметр data) для передачи в колбэк-финализатор.

Потоки могут вызывать JavaScript через [Non]BlockingCall. Это добавит запись в очередь нижележащей thread-safe функции, чтобы обработать её асинхронно в главном потоке во время его обработки event loop.

Несколько потоков могут одновременно использовать thread-safe функцию. Thread-safe функция управляет своим жизненным циклом через подсчёт числа потоков, активно её использующих. Это число начинается с параметра начального числа потоков в New(), увеличивается через Acquire() и уменьшается через Release(). Как только число активных потоков достигает нуля, thread-safe функция уничтожается, запуская колбэк-финализатор в главном потоке, если он предоставлен.

Вот два общих подхода к использованию thread-safe функций в приложениях:

Если число потоков известно на момент создания thread-safe функции, установите параметр initial_thread_count в это число при вызове New(). Каждый поток будет иметь собственный доступ к thread-safe функции, пока не вызовет Release(). Как только все потоки вызвали Release(), thread-safe функция уничтожается.

Другой распространённый сценарий — динамически создавать и уничтожать потоки на основе различной логики во время выполнения. Один из способов справиться с этим сценарием — предоставить несколько нативных JavaScript-функций, взаимодействующих с API thread-safe функций, следующим образом:

  1. Создание thread-safe функции через New() с начальным числом потоков 1.
  2. Вызов Acquire() и создание нового нативного потока. Новый поток теперь может использовать [Non]BlockingCall().
  3. Инициирование очистки/уничтожения, например путём...
  • вызова Abort() и того, чтобы каждый поток вызвал либо [Non]BlockingCall(), либо Release()
  • использования пользовательской логики с другими thread-safe API, чтобы гарантировать, что все потоки вызовут Release(), чтобы уменьшить число активных потоков до 0.

Этот пример предоставляет единственную функцию, которая создаёт thread-safe функцию и нативный поток. Функция возвращает промис, который разрешается после того, как нативный поток десять раз вызовет JavaScript. Пример состоит из трёх файлов исходников: binding.gyp конфигурирует сборку, addon.cc реализует нативный модуль, а addon.js прогоняет его из JavaScript.

Запуск addon.js производит вывод, похожий на:

2019-11-25T22:14:56.175Z 0
2019-11-25T22:14:56.380Z 1
2019-11-25T22:14:56.582Z 2
2019-11-25T22:14:56.787Z 3
2019-11-25T22:14:56.987Z 4
2019-11-25T22:14:57.187Z 5
2019-11-25T22:14:57.388Z 6
2019-11-25T22:14:57.591Z 7
2019-11-25T22:14:57.796Z 8
2019-11-25T22:14:58.001Z 9
true

По умолчанию Node будет ждать финализации thread-safe функции, прежде чем очиститься и выйти. См. Управление потоками. Это поведение можно изменить вызовом Unref(), позволяя Node очиститься, не дожидаясь, пока число потоков достигнет нуля. Вызов Ref() вернёт thread-safe функцию к прежнему поведению выхода, требуя, чтобы её Release()-нули и/или Abort()-нули все использующие её потоки.

Нет. Возвращаемое значение napi_closing должно означать для потока, что thread-safe функцию больше нельзя использовать. Это включает и вызов Release().