#c #node.js #c 11 #v8 #node.js-addon
#c #node.js #c 11 #версия 8 #node.js-дополнение
Вопрос:
Я разрабатываю Node.js приложение, которое включает в себя Windows DLL. Библиотека DLL управляет научным оборудованием для контекста.
Мой интерфейс от узла к DLL работает нормально, однако в DLL есть некоторые недетерминированные вызовы, которые зависят от топологии сети и радиосигналов в комнате. Эти вызовы могут занимать от 10 секунд до 10 минут.
Я бы хотел отключить эти вызовы от цикла событий узла и даже избежать AsyncWorkers. Я бы хотел поместить их в их собственные потоки C . Меня беспокоит, что я недостаточно знаю Node / V8, чтобы правильно подойти к проблеме, хотя я уже дважды пытался.
Ниже приведена моя попытка создать поток для вызова обратного вызова js, хотя я не уверен, что это хороший подход. Мне нужен результат вызова, и то, что у меня пока есть, — это «демон» в моем приложении node, который регулярно проверяет, чтобы получить результаты для выполненных задач.
mTp
во фрагменте ниже приведена реализация threadpool, которую я написал. Runtask принимает лямбда-код C в качестве параметра, который будет помещен в мою очередь рабочего потока. mThreadStatus — это отображение из моего дескриптора потока, который является строкой, в thread_status_t enum. mThreadResults — это другое сопоставление дескриптора потока с v8::Value, которое возвращается обратным вызовом.
void
MyObj::SpawnThread(functionInput info) {
MyObj* obj = ObjectWrap::Unwrap<MyObj>(info.Holder());
obj->mTp.RunTask([amp;]() {
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] = resu<
}
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 очень ценится. Спасибо!
Ответ №1:
Я не решал подобную проблему раньше, но общий способ, которым я бы это сделал, таков:
- пусть код JavaScript не обращает внимания на потоковую обработку
- предоставить функцию
getMeasurements(callback)
JavaScript, реализованную на C - когда функция вызывается, она сама получает поток (либо недавно созданный, либо из пула) и инструктирует его выполнить блокирующий внешний вызов; когда этот вызов завершен, поток передает свой результат главному потоку, который вызывает
callback
с ним. - таким образом, вся связь с кодом JavaScript (т. Е. все взаимодействие с V8) происходит в главном потоке, и вы используете фоновые потоки только для блокирующих вызовов.
Я надеюсь, это поможет!
Комментарии:
1. Это очень полезно. Спасибо!
2. Я думаю, что часть, в которой я нечетко разбираюсь, заключается в том, как передать его результат в основной поток? Вы знаете, есть ли способ сделать это с V8? Я предполагаю, что на этом этапе я создаю AsyncWorker, но я не уверен, как это сделать без другой точки входа в мой код C из js
3. V8 вообще не участвует в работе с перекрестными потоками. Сохраняйте обратный вызов в главном потоке. Основной поток должен иметь какой-то основной цикл / «цикл событий», где он ожидает запросов от JavaScript и / или от таймеров, и именно там он также должен проверять, указал ли какой-либо фоновый поток, что он выполнил свою задачу.
4. Разобрался с этим, мне пришлось создать AsyncWorker в версии 8 (используя NAN). Это позволило мне использовать обратный вызов, который я передал в функцию, для выполнения следующих шагов. Спасибо за вашу помощь, это вывело меня на правильный путь!