Блокировка звонков в аддоне Node.js
Я разрабатываю приложение Node.js, которое включает в себя Windows DLL. DLL управляет научным оборудованием, для контекста.
Мой интерфейс от Node к DLL идет хорошо, однако DLL имеет некоторые недетерминированные вызовы, которые зависят от топологии сети и радиосигналов в комнате. Эти звонки могут занять от 10 секунд до 10 минут.
Я хотел бы получить эти вызовы из цикла событий Node и даже избегать AsyncWorkers. Я хотел бы поместить их в свои собственные потоки C++. Я обеспокоен тем, что не знаю достаточно Node/V8, чтобы правильно подойти к проблеме, хотя я пытался дважды.
Ниже приведена моя попытка создать поток для вызова обратного вызова js, хотя я не уверен, что это хороший подход. Мне нужен результат вызова, и у меня до сих пор есть "демон" в приложении моего узла, который регулярно проверяет результаты поиска для выполненных задач.
mTp
ниже приведен фрагмент реализации пула потоков, который я написал. Runtask принимает лямбда-код C++ в качестве параметра для добавления в мою очередь рабочих потоков. mThreadStatus - это карта из моего дескриптора потока, который является строкой, в перечисление 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()
})
И вот так, чтобы получить результат, в моем "демоне":
var status = this.myObj.getThreadStatus('startMeasurements')
if ( status === 'complete') {
// Publish returned information to front-end
}
else if (status === 'error') {
// Handle error
}
Кто-нибудь решил эту проблему раньше? Это похоже на достойный подход? Помощь с v8 очень ценится. Спасибо!
1 ответ
Я не решал подобную проблему раньше, но общий способ, которым я хотел бы пойти по этому поводу:
- пусть код JavaScript не обращает внимания на потоки
- выставить функцию
getMeasurements(callback)
в JavaScript, реализованный в C++ - когда функция вызывается, она сама получает поток (либо вновь созданный, либо из пула) и инструктирует ее выполнить блокирующий внешний вызов; когда этот вызов завершен, поток сообщает свой результат основному потоку, который вызывает
callback
с этим. - таким образом, все взаимодействие с кодом JavaScript (т. е. все взаимодействие с V8) происходит в основном потоке, и вы используете только фоновые потоки для блокирующих вызовов.
Надеюсь, это поможет!