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 функций, следующим образом:
- Создание thread-safe функции через
New()с начальным числом потоков1. - Вызов
Acquire()и создание нового нативного потока. Новый поток теперь может использовать[Non]BlockingCall(). - Инициирование очистки/уничтожения, например путём...
- вызова
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().