在 Node.js 插件中阻止调用
Blocking calls in a Node.js Addon
我正在开发一个包含 Windows DLL 的 Node.js 应用程序。 DLL 根据上下文管理科学设备。
我从 Node 到 DLL 的接口运行良好,但是 DLL 有一些不确定的调用,这些调用取决于房间中的网络拓扑和 RF 信号。这些呼叫可能需要 10 秒到 10 分钟。
我希望将这些调用从 Node 的事件循环中移除,甚至避免使用 AsyncWorkers。我想把它们放在它们自己的 C++ 线程中。我担心自己 Node/V8 的知识不足以正确解决问题,尽管我现在已经尝试了两次。
下面是我尝试生成一个线程来调用 js 回调的尝试,但我不确定这是否是一个好方法。我需要调用的结果,到目前为止,我的节点应用程序中有一个 'daemon',它定期检查以检索已完成任务的结果。
下面代码片段中的 mTp
是我编写的线程池实现。 Runtask 将 C++ lambda 作为参数推送到我的工作线程队列中。 mThreadStatus 是从我的线程 'handle' 到 thread_status_t 枚举的映射,它是一个字符串。 mThreadResults 是从线程句柄到回调返回的 v8::Value 的另一个映射。
void
MyObj::SpawnThread(functionInput info) {
MyObj* obj = ObjectWrap::Unwrap<MyObj>(info.Holder());
obj->mTp.RunTask([&]() {
v8::Isolate::CreateParams cp;
v8::Isolate* tpIsolate = v8::Isolate::New(cp);
v8::Locker locker(tpIsolate);
v8::Isolate::Scope isolateScope(tpIsolate);
Nan::HandleScope scope;
auto global = obj->mContext.Get(tpIsolate)->Global();
auto handle = std::string(*v8::String::Utf8Value(info[0]->ToString()));
{
std::unique_lock<std::shared_mutex> lock(obj->mThreadStatusMutex);
obj->mThreadStatus[handle] = thread_status_t::running;
}
v8::Handle<v8::Function> f = v8::Handle<v8::Function>::Cast(info[1]);
v8::TryCatch trycatch(tpIsolate);
v8::Handle<v8::Value> result = f->Call(global, 0, nullptr);
if (result.IsEmpty()) {
v8::Local<v8::Value> exception = trycatch.Exception();
std::unique_lock<std::shared_mutex> lock(obj->mThreadStatusMutex);
obj->mThreadStatus[handle] = thread_status_t::error;
return;
}
{
std::unique_lock<std::shared_mutex> resultLock(obj->mThreadResultsMutex);
obj->mThreadResults[handle] = result;
}
std::unique_lock<std::shared_mutex> lock(obj->mThreadStatusMutex);
obj->mThreadStatus[handle] = completed;
tpIsolate->Dispose();
});
我设想我的 js 看起来像这样来生成一个线程:
var ctx = this
this.myObj.spawnThread('startMeasurements', () => {
return ctx.myObj.startMeasurements()
})
像这样得到结果,在我的 'daemon':
var status = this.myObj.getThreadStatus('startMeasurements')
if ( status === 'complete') {
// Publish returned information to front-end
}
else if (status === 'error') {
// Handle error
}
有没有人解决过这个问题?这看起来像一个体面的方法吗?非常感谢 v8 的帮助。谢谢!
我以前没有解决过类似的问题,但我的一般做法是:
- 让 JavaScript 代码忽略线程
- 将函数
getMeasurements(callback)
公开给 JavaScript,用 C++ 实现
- 调用该函数时,它会为自己获取一个线程(新创建的或来自池中的线程)并指示它进行阻塞的外部调用;当该调用完成时,线程将其结果通知主线程,主线程用它调用
callback
。
- 这样,与 JavaScript 代码的所有通信(即与 V8 的所有交互)都发生在主线程上,并且您只使用后台线程进行阻塞调用。
希望对您有所帮助!
我正在开发一个包含 Windows DLL 的 Node.js 应用程序。 DLL 根据上下文管理科学设备。
我从 Node 到 DLL 的接口运行良好,但是 DLL 有一些不确定的调用,这些调用取决于房间中的网络拓扑和 RF 信号。这些呼叫可能需要 10 秒到 10 分钟。
我希望将这些调用从 Node 的事件循环中移除,甚至避免使用 AsyncWorkers。我想把它们放在它们自己的 C++ 线程中。我担心自己 Node/V8 的知识不足以正确解决问题,尽管我现在已经尝试了两次。
下面是我尝试生成一个线程来调用 js 回调的尝试,但我不确定这是否是一个好方法。我需要调用的结果,到目前为止,我的节点应用程序中有一个 'daemon',它定期检查以检索已完成任务的结果。
下面代码片段中的mTp
是我编写的线程池实现。 Runtask 将 C++ lambda 作为参数推送到我的工作线程队列中。 mThreadStatus 是从我的线程 'handle' 到 thread_status_t 枚举的映射,它是一个字符串。 mThreadResults 是从线程句柄到回调返回的 v8::Value 的另一个映射。
void
MyObj::SpawnThread(functionInput info) {
MyObj* obj = ObjectWrap::Unwrap<MyObj>(info.Holder());
obj->mTp.RunTask([&]() {
v8::Isolate::CreateParams cp;
v8::Isolate* tpIsolate = v8::Isolate::New(cp);
v8::Locker locker(tpIsolate);
v8::Isolate::Scope isolateScope(tpIsolate);
Nan::HandleScope scope;
auto global = obj->mContext.Get(tpIsolate)->Global();
auto handle = std::string(*v8::String::Utf8Value(info[0]->ToString()));
{
std::unique_lock<std::shared_mutex> lock(obj->mThreadStatusMutex);
obj->mThreadStatus[handle] = thread_status_t::running;
}
v8::Handle<v8::Function> f = v8::Handle<v8::Function>::Cast(info[1]);
v8::TryCatch trycatch(tpIsolate);
v8::Handle<v8::Value> result = f->Call(global, 0, nullptr);
if (result.IsEmpty()) {
v8::Local<v8::Value> exception = trycatch.Exception();
std::unique_lock<std::shared_mutex> lock(obj->mThreadStatusMutex);
obj->mThreadStatus[handle] = thread_status_t::error;
return;
}
{
std::unique_lock<std::shared_mutex> resultLock(obj->mThreadResultsMutex);
obj->mThreadResults[handle] = result;
}
std::unique_lock<std::shared_mutex> lock(obj->mThreadStatusMutex);
obj->mThreadStatus[handle] = completed;
tpIsolate->Dispose();
});
我设想我的 js 看起来像这样来生成一个线程:
var ctx = this
this.myObj.spawnThread('startMeasurements', () => {
return ctx.myObj.startMeasurements()
})
像这样得到结果,在我的 'daemon':
var status = this.myObj.getThreadStatus('startMeasurements')
if ( status === 'complete') {
// Publish returned information to front-end
}
else if (status === 'error') {
// Handle error
}
有没有人解决过这个问题?这看起来像一个体面的方法吗?非常感谢 v8 的帮助。谢谢!
我以前没有解决过类似的问题,但我的一般做法是:
- 让 JavaScript 代码忽略线程
- 将函数
getMeasurements(callback)
公开给 JavaScript,用 C++ 实现 - 调用该函数时,它会为自己获取一个线程(新创建的或来自池中的线程)并指示它进行阻塞的外部调用;当该调用完成时,线程将其结果通知主线程,主线程用它调用
callback
。 - 这样,与 JavaScript 代码的所有通信(即与 V8 的所有交互)都发生在主线程上,并且您只使用后台线程进行阻塞调用。
希望对您有所帮助!