ThreadSafeFunction
原文链接: https://github.com/nodejs/node-addon-api/blob/main/doc/threadsafe_function.md
The Napi::ThreadSafeFunction
type provides APIs for threads to communicate
with the addon's main thread to invoke JavaScript functions on their behalf.
Documentation can be found for an overview of the API, as well
as differences between the two thread-safe function
APIs.
1. 方法
1.1. 构造函数
创建一个新的 Napi::ThreadSafeFunction
实例.
Napi::Function::ThreadSafeFunction();
1.2. 构造函数
创建一个新的 Napi::ThreadSafeFunction
实例.
Napi::ThreadSafeFunction::ThreadSafeFunction(napi_threadsafe_function tsfn);
tsfn
:napi_threadsafe_function
指向一个现有线程安全函数的句柄。
当使用这个构造函数的时候, 仅能使用 Blocking(void*)
/ NonBlocking(void*)
重载函数;
Callback
和 data*
也不能使用. 参阅下文了解更多详细信息。
1.3. New
创建一个新的 Napi::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);
env
: 构建Napi::ThreadSafeFunction
对象的napi_env
环境。callback
: 从另一个线程调用的函数。resource
: 可选, 与 async worker 关联的对象, 会被传递给 async_hooks init hooksresourceName
: 一个 JavaScript 字符串,用于为 async_hooks API 提供的诊断信息提供资源类型的标识符maxQueueSize
: 队列的最大大小.0
表示没有限制.initialThreadCount
: 将使用此函数的初始线程数(包括主线程)。context
: 可选,附加到ThreadSafeFunction
结果的数据。可以通过调用GetContext()
·` 来获取。finalizeCallback
: 可选,当ThreadSafeFunction
被销毁时调用。当线程安全函数即将被销毁时,此回调将在主线程上调用。data
: 可选,传递给finalizeCallback
的数据.
返回一个非空的 Napi::ThreadSafeFunction
实例.
1.4. Acquire
向该线程安全函数对象添加一个线程,表示一个新线程将开始使用该线程安全函数。
napi_status Napi::ThreadSafeFunction::Acquire() const
返回以下结果之一:
napi_ok
: 该线程已成功获取可供其使用的线程安全函数。napi_closing
: 该线程安全函数已经被先前的Abort()
调用标记为关闭中。
1.5. Release
表示现有线程将停止使用此线程安全函数。线程停止使用此线程安全函数时,应调用此 API。
调用此 API 后使用任何线程安全 API 都会导致当前线程产生未定义的结果,因为当前线程可能已被销毁。
napi_status Napi::ThreadSafeFunction::Release() const
返回以下之一:
napi_ok
: 线程安全函数已成功释放。napi_invalid_arg
: 线程安全函数的线程数为0
napi_generic_failure
: 尝试释放线程安全函数时发生一般错误。
1.6. Abort
"中止" 线程安全函数。这将导致与该线程安全函数关联的所有后续 API(除 Release()
外)在其引用计数达到零之前返回 napi_closing
。尤其是,BlockingCall
和 NonBlockingCall()
将返回 napi_closing
,以此来通知线程不能用线程安全函数进行异步调用。这可以用作终止线程的条件。当线程安全函数调用的返回值为 napi_closing
时,线程必须
停止使用该线程安全函数,因为不再保证
该函数会被分配。
napi_status Napi::ThreadSafeFunction::Abort() const
返回以下结果之一:
napi_ok
: 此线程安全函数已成功被中止.napi_invalid_arg
: 线程安全函数的线程数为0
napi_generic_failure
: 尝试中止线程安全函数时发生一般错误。
1.7. BlockingCall / NonBlockingCall
以阻塞或非阻塞方式调用 Javascript 函数。
BlockingCall()
: 此 API 会阻塞,直到队列中有可用空间。 如果创建线程安全函数时的最大队列大小为0
,则不会阻塞。NonBlockingCall()
: 如果队列已满,将返回napi_queue_full
,从而导致数据无法成功添加到队列。
BlockingCall()
和
NonBlockingCall()
函数对于各种可选参数有几种重载:
这些特定的函数重载只能用于通过 ThreadSafeFunction::New
创建的 ThreadSafeFunction
实例。
napi_status Napi::ThreadSafeFunction::BlockingCall(DataType* data, Callback callback) const
napi_status Napi::ThreadSafeFunction::NonBlockingCall(DataType* data, Callback callback) const
data
: 可选, 传递给callback
回调的数据.callback
: 可选, 在主线程上调用的 C++ 函数。回调函数接收ThreadSafeFunction
的 JavaScript 回调函数,并将其作为Napi::Function
的参数进行调用,同时接收DataType*
数据指针(如果提供了的话)。必须实现void operator()(Napi::Env env, Function jsCallback, DataType* data)
. 无需通过MakeCallback()
调用 JavaScript,因为 Node-API 在适合回调的上下文中运行callback
回调。
这些特定的函数重载只能用于通过 ThreadSafeFunction(napi_threadsafe_function)
创建的 ThreadSafeFunction
实例。
napi_status Napi::ThreadSafeFunction::BlockingCall(void* data) const
napi_status Napi::ThreadSafeFunction::NonBlockingCall(void* data) const
data
: 通过napi_create_threadsafe_function
创建线程安全函数时传递给call_js_cb
的数据。
返回以下结果之一:
napi_ok
: 该调用已成功添加到队列。napi_queue_full
: 尝试调用非阻塞方法时,队列已满。napi_closing
: 线程安全函数已中止,无法接受更多调用。napi_invalid_arg
: 线程安全函数已关闭。napi_generic_failure
: 尝试添加到队列时发生一般错误。
2. 例子
#include <chrono>
#include <thread>
#include <napi.h>
using namespace Napi;
std::thread nativeThread;
ThreadSafeFunction tsfn;
Value Start( const CallbackInfo& info ) {
Napi::Env env = info.Env();
if (info.Length() < 2) {
throw TypeError::New( env, "Expected two arguments" );
} else if (!info[0].IsFunction()) {
throw TypeError::New( env, "Expected first arg to be function" );
} else if (!info[1].IsNumber()) {
throw TypeError::New( env, "Expected second arg to be number" );
}
int count = info[1].As<Number>().Int32Value();
// Create a ThreadSafeFunction
tsfn = ThreadSafeFunction::New(
env,
info[0].As<Function>(), // JavaScript function called asynchronously
"Resource Name", // Name
0, // Unlimited queue
1, // Only one thread will use this initially
[]( Napi::Env ) { // Finalizer used to clean threads up
nativeThread.join();
}
);
// Create a native thread
nativeThread = std::thread([count] {
auto callback = []( Napi::Env env, Function jsCallback, int* value ) {
// Transform native data into JS data, passing it to the provided
// `jsCallback` -- the TSFN's JavaScript function.
jsCallback.Call( {Number::New( env, *value )} );
// We're finished with the data.
delete value;
};
for (int i = 0; i < count; i++) {
// Create new data
int* value = new int(clock());
// Perform a blocking call
napi_status status = tsfn.BlockingCall(value, callback);
if (status != napi_ok) {
// Handle error
break;
}
std::this_thread::sleep_for(std::chrono::seconds(1));
}
// Release the thread-safe function
tsfn.Release();
});
return Boolean::New(env, true);
}
Napi::Object Init( Napi::Env env, Object exports ) {
exports.Set("start", Function::New( env, Start ));
return exports;
}
NODE_API_MODULE( clock, Init )
上述代码可以从 JavaScript 中使用,如下所示:
const { start } = require('bindings')('clock');
start(function () {
console.log("JavaScript callback called with arguments", Array.from(arguments));
}, 5);
执行后,输出将以一秒的间隔显示五次 clock()
的值:
JavaScript callback called with arguments [ 84745 ]
JavaScript callback called with arguments [ 103211 ]
JavaScript callback called with arguments [ 104516 ]
JavaScript callback called with arguments [ 105104 ]
JavaScript callback called with arguments [ 105691 ]